From ec2bccc37687ab8d4059ded15b9233f9928c69dc Mon Sep 17 00:00:00 2001 From: ipvsean Date: Sat, 25 Apr 2020 09:50:41 -0400 Subject: [PATCH 01/18] for Roland My favorite person ever @liquidat --- README.md | 1 + docs/faq.md | 4 +++ docs/release.md | 58 ++++++++++++++++++++++++++++++++++++++++++ images/ci.png | Bin 0 -> 115233 bytes images/passed.png | Bin 0 -> 49230 bytes images/release_pr.png | Bin 0 -> 17794 bytes 6 files changed, 63 insertions(+) create mode 100644 docs/release.md create mode 100644 images/ci.png create mode 100644 images/passed.png create mode 100644 images/release_pr.png diff --git a/README.md b/README.md index 4fd389699..b2f3eebc8 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ The Red Hat Ansible Automation Workshops project is intended for effectively dem - [How to contribute](docs/contribute.md) - [How to use the AWS Lab Provisioner](provisioner/README.md) - [FAQ](docs/faq.md) + - [Release Process](docs/release.md) --- ![Red Hat Ansible Automation](images/rh-ansible-automation-platform.png) diff --git a/docs/faq.md b/docs/faq.md index e3d5135ec..6079ff1a6 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -1,6 +1,10 @@ # FAQ for the Provisioner Frequently Asked Questions... or rather common problems that people have hit. +## How do I do a release PR? + +Read this [guide](release.md) + ## Problem: boto3 missing ``` diff --git a/docs/release.md b/docs/release.md new file mode 100644 index 000000000..fec3ef0f4 --- /dev/null +++ b/docs/release.md @@ -0,0 +1,58 @@ +# Release PR + +The Ansible Automation Workshops have two main branches + +- `master` - this is the stable supported branch. This is what RHPDS (Red Hat Product Demo System) points to +- `devel` - this is the development branch. This is where PRs (Pull Requests) go into, and get tested. + +A **release** is when we move `devel` into `master`. This means the stable `master` branch will pickup all new features, bug fixes and changes that `devel` branch has been testing. + +# How to push a release + +1. An administrator has to create a PR (pull request) from `devel` into `master` from `https://github.com/ansible/workshops` + + ![release pr](../images/release_pr.png) + +2. Title the PR with `Date Release PR` + + For example: + `April 16th, 2020 Release PR` + +3. Copy the last PR's contents into your new PR contents, here is an example: [https://github.com/ansible/workshops/pull/800](https://github.com/ansible/workshops/pull/800) + +e.g. here is a template you can use + +``` +# SUMMARY + +this is a release PR, a release PR merges the devel branch to the master branch taking in multiple PRs over the last period of time. The last release was on April 16th, 2020 https://github.com/ansible/workshops/pull/784 + +this merges PRs for the following since the last review: + +- https://github.com/ansible/workshops/pull/785 - cleaning up tower exercises +- https://github.com/ansible/workshops/pull/786 - Fix start of YAML marker +- https://github.com/ansible/workshops/pull/790 - add synchronization with ansible/product-demos +- https://github.com/ansible/workshops/pull/795 - new exercise for rhel lab with system roles +- https://github.com/ansible/workshops/pull/799 - fixing RHEL workshop exercises + +# ISSUE TYPE +Release PR + +cc @cloin @liquidat @goetzrieger @Spredzy +``` + +4. The goal of the PR summary is to: + + - indicate this is a release PR + - link to the last release PR + - summarize all PRs into `devel` since the last time there was a release (this means only purple merged PRs, not closed PRs, or PRs still not merged yet) + +5. wait for CI testing to complete + + ![ci testing](../images/ci.png) + +6. merge when you get passing CI + + ![passed ci](../images/passed.png) + +7. If there are major changes, announce these to Roland to make sure they reach his newsletter :) diff --git a/images/ci.png b/images/ci.png new file mode 100644 index 0000000000000000000000000000000000000000..a48a7f4d02667ad29af104dbe03f73343b45ea88 GIT binary patch literal 115233 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

#J z5Bat^V#r-B2jel9q}DiYd=PbzaitTCE&)Upnp3h>24Z1nk`j@L!hI zr`cx5Z@D3r5BXa}`ucM>SdZk0#(7Q5TP%Y|JiXzl9FMVVHU?f4DrImJak}`B1}ZH8 z_k5_+WSGLJAW5K(S; zo+$(NL7>w0JEbpMwVT%U5WPHHrj+lC7INPA@rMkjjRh{c<#E~X%Zz4!4hX$NClg{W zl_lV3l(Qna$H|B^TWAuL&J>FHoLJ=++m)4_NW^KIflR>GJIiW56MxCKzj^|B7zKBU z1_+$ySGr%IH1z6zMa8Gz=O)%@a^QErb&RCHJ3-C)l&aP_C1I>a#^7>u1kl~+!8atU zQpaCDuUq%R%?<9bXn*9SZ?Y#93iw3G`Uy3ueR!xq)_o*~Mse+u?`jQ`<6NOp}x7!!7iMTM`*b7c75n9rs)B?A~L0a@(S_AV71A@_@|e^u4TTu zRd03*o$}}*_ktIQWcvC%tEHuVx51UZDKS26`CBZ7WNMK9x+I648I9|;B$a8RWb%gm ze8;UuzLaQLS5q2~eV9b*t>|ltaGJ%lk1O-Y#Xf8E2%lTk5m4F87O;p-B}3^c#3QHH zhl#ShdDUR|e|q*N%5$D+wy z)ep%tJpq+1^mYbf^g+g4=pLFH=oc!&vxMU=-lm}%q%Csot46*KRI4TWQL{Z)v%|KP z0I%OQoL_X*?0Dh_G(Mx>;9=^*wdJN`&}_k&RL{y^fw+5J?%qF0r^oR+pC_|Ze7L=| zq}rHGgLrD35-VKZ3@C52IYjSH<0`+C1;vW1ogc3ii~*S&c#ib$3v=V?63B69#q8ft z2*n3r_)38V@AJLAd6C&LeJN7;wNcgB-u-yw`< zYQp)SBuiy&pE~Et^SJ!(VYl6gynMUNf9AY!e>1K>P2c(N_!7(BW=ZM}8|0P?)8vD}f)z^Hti1g6pN$?t^EwJ3@;YF%K~Z;y+#E(TqeE3s(| z3%{qNBYnDdWL*j)Vo9!aU2aOW+`olkwm5vN9D~Whpz+o}vBpF|IGm$NOO7BAlRUGX z)W1EB!#ZD}n~mVkrO_tJE5!0}Psm0)+eoSmg(Y8evKOsME~FVqGZZ@GbK^0>g9ajL zeP4qMMCVM%*NM30;<6S?h_^s)Nq5<&A$P77^SGtR8yuSS`|W6Y`eF3560-bTGfbZJOU) z%!`BHlb_i-Y_NuG`B!3x=oX^7v~ib3QH12^mCXE5euY$!Nl~STUL0(W+Guf<$w}AR zIuw=UWVb`DbXG&#aGFLTDcwcwwj5+Uj9k7+NXFEc_c2PTBd_FLo{WMGWO zvpM$W-P`i5EDj`sUW`o}boBU-1hI(k`(OR-ITAK;M59gJT;9<9s~l@2#k zPYyZQxbW)fCDmAj@(yt;=-&QWDo9&dk3k@CystE3esVJ~Y*v$4BR`qrQ2%9_n`}41 zk~Est>5N1O;xpCv_2=41cU7O?kRRPl|LO!yT$Ij2ZV6Y;ax`k?+gJ`+Sfi0_jc=hx zu5jVmW26WcGm_eaVB!W|j73R;aIBK} zF0X6&OmV!nSafekGKF$>HC=?x+=@38K5jI@(&AGsr_8Q!EPv?}C-@n=LPjF%8@fRA z(*5o?29f>|7ZAzY5^g8`;Dg#PD|hh|fmUn^oI~Twe(Qtr+%ngs;9%`g`MvSZ zn1GpLfs|hhgY-vj#+HLXskjm@PHt!#MM`nLXiDnggvIc<75nY(K`x}>p#O)xvkr=D z+tz(T1PdPA65QQ2xCRTZ!Gl`}Zb2F+xCVk-aCdiUG`PEVqmA=c_SyT~^KPAe@4xrG zx>Ze8LsP3~_gZ7kImh_E-!KtPVmuRb7`!({P2P1M2(77hzGX6wbSrIrETvInxb=5n zu3_~#LHcbpc^w1~JCcme2WPe^WW?vYwK{r6$6L<)WXM>WGzW*l%u7%79dpGIv6T9_ zMraJbUKPn~q-OY|q}Gn#yw~}j{_I#W==?Dj97)egS8m>@H`J@zrp50@O7dY3LY!XH z8?j;wZQ?N=6>Z2r;*Dfk$qCZ zBW0v`b`f3?H%v9SAw}7eNajR7xWIzxXXYPG>}sugQUl=?1&D{S9x^?IhEw@Ljn`i|(S|ne zwM0MD&~?M>TU@*S0_XTKNsXpeZDv;&JwtV5qd&o(CI} ziBHw_zFm4ZvflY9ZoMPu0mI)JAza|HK`&OvA_P6&9M~0U<+loVUg~ta-wL6rGHyr1j*ZG zIHY}0_|Zn&oh+yKPkNY^{6}MU4>bE-O4N?c(&rQc5Il%d}j?k6P z#0PyJ2elV-#k*6g&063!5ee1stsGC68^G+bnEmGG$j?J+w&62EF#T!S6ZE!6%_A+T zNhbTHLjITFW}K10cQ~4cc?`M*My=e)Pj1nORP(pE2{&WIc8;4!2l&kM8i=7}e~-sL z$kLv=f{f#A2L2AIMHGTejvMl5#EahizQNpk3AsGLsbT152)L0o^@DHV-oiDW@hNYV z@25t5y8uL%y<@LCHwt{pm?S5uA2Ex&LA>fARTcL?cZ-WV>B~KwkI*TyotJt`wgipC z)YhU%1i8X1wl7D>;4)Txpe*k(gEW~A-LjjVH=mx2XFKt)ah``@}D|iRJuhNmbocF+0J|$*eqD6A`uO z`)RJ6Y!`P}Th$J8bXG8*`GeW99)IoW4t|~WG@o{rcWgsr1sG8C6D#ZOjGHrF?n`wc3Ttjiya?ft|KAV?$qlE2b zG9S{J^dAt)zncO|%S`DMn2e&fNl%3L1uDXP@T4kkaO-wC>(*%+@h zcffV0Ne=8S$deN0b3I%J@5{F^z_^{Sx@D5=sReYg)@26Pa7`kSv)@iijw)tN45}B> z7+poln|Ipc0%@4PG^?(~&~{#8rqWTIpV#tg+gI?ZXPF;Bm(?Z%>mMWdwJJ9|Y24ls z1SBx&$=YbwvCYplo6?((rX`PtLglW{cGx3r)vH*bJK-V zydeAeM15)5HrEc>SRnlL6GBMqK&ljh)$Gta{EI73Af>t~YpGVJ*ydOLX+Wh$j(ENX zrx!H59`zts?K;5Lr8$D_YTAUfwhjQ!IT*Mcy)JSQEYM>w5ydK&sd1p!jd@layA*12falz4Jlj-MBc#)z1!(rbMtJv>bI%t^B15IYQ+f2!lG0Vzr7^3-$#yenhdHbB5Bfl{ z<@VL?_{M_(GgTy^jcYBCL5m6X(ijYRj~%U@TO_0$qb0jjkUPK*s`+`&)wBW>&+A z*96>#F%I&OSckE$h=C9Jle-n(>7N+Puo>@4JZrt@s{@Zi(XEP9i&c_m&-}x#Ta=tN zfAmv`X5G_vS|?}tGZN0=ug(0vmrUDT2vy~))ezJ~?D+CR*+-mSt4nY%;xHveSfR$?diy5*UsRX*2g=*$QV zHt@Oi2dqA(!5G^~ckDZiS;S43_%l2Gm%OG!DH3U2>?AoR=()%lN;*o9{>2;fH&#rDk_`)PFf(1i~X1=)sa*0jRf z-|p$PgI^~OZuQ8zR=9r;YemX{X^jm~ACHhdVSXabxcuI%zj&&Fm`T9a)?XuyJ!7kQ zCam0NXRG0EC3x&zja9$RgX_&XKBvVIddJ-4SCnQ3CHs{xhjUVL31!jFZmqLPqg%7g z)SsqYL4GMwk2H>M<-46(16iiUIWsS2jKMhRVQ9JK_P=Uo-r-xyUL?2Z4aU>S5V^CK zv$?FX@n-1SohD_n>Be6ZxHu0(;DrrdoOv+{lBky~(0Ie&ZA`ruHs2bGi_5I8wO%+n z_k54X+h0%|=I8mX#bA~&5J)3of;Ry-ZiI`NUN?BhJeeKIaPL)#?rrQ_)u}SQ??|4p zQfk>Xgj|yo@sTSXdyk3gLQZ!K={V^DfH0UXo!6{CD}ydwXj1S7C3=G^EUAe2vR;ew zrZ2E1zV(_fkuUHsmAfnT^RILQafD9MsA+C=w~cx4EpO-|$eZ6%2Qdkrk`Xsu$)^{8 zW5=29$I=O)4t;m4Ae_5gY>O9Cgv%lGw&$q>V#QT^;0t8iY?EJjn>u`%0yxy}uA%e~ z8oAJ6-IaA1Kg`Ome){^8~7%L41D}9qq75Nc1!n zTq0pezdU<~euWw3l%oH%TDg$mEY`?w@qj%qCTwk8D$sHQt!0W{pMHj}KF$9A5`|C& zvW&W+h;10Y(&SR+Fgh7wG7y7EegjTdchQN`@QHG>3J#}nRD4Cq(QeUGHb=PLo=!~W z?Z_~ft_9$Wm(sK*X?y!na~~^%VPHvpwc*Yl?iXQrL8DwHDIc<5UZ|-hcV+6qle(X* z_?&8jYD;o66I{NM36zUOi{;ywA~K~t%hd0p`g~ZwVyW%*qG7JR=vBNYNFUk zM^vVTujV?Aq4}mwdxS5DwMz85a;i=P&3XQ6TS>&ad%IK8jEn1ll4l^-Eo}$~jQg@S zuCg#+F(dSwIA>Zo^!AG#x2W%P0K`{2=kMP`*mAg3mqEa3V@PBKd5Pno`DU%t;ciHy z$ZVLE6d7eL)3}LCwQe+Ov6+EE`{J1U-O=EV*{^e2$BdlXo;-ZTDAKs|sqhDVpfZIXK z&}cI95br>{V%3!rANR(OoB{#FW;tE!kHHrThAcI_C%_niyx?Xt+q9=snZ<|%l^xBu zs zuUwXPLpxW~db*q0^V^~`1{_x7)}7VXrJhcIXs3rU*U{s3@N2oe_jqe%!v0d>v1IS( zISLC+vD#Mribi19sb@y#BrL-NXMq{TX@EjIrUxl(r73bns9Wdft!v2TTGw57Yvls=RNu-LjJGn21n+hPalg;d9y|Ej73(-l0ZHl%#Sy?nf<~ zk7BU`R4C_UDKLjdI+usg(XxjU(@hdnI(tjGIaXA14<8O3Z@}xn3bD;ra3a_wH;CDH z^>vchw9c{0d)BC3x0PLJ1wD(!jyxA-@JQRfcwYp7G73tJP z@@<`PulRSN1bkVGhi%cRv)XnQf2}gV6+H$jpL6VaH$xlkFF$fUFFxG~F?NjAz%hFe z0P!&hK9hV!eE2$afX8AU0dN2sdd<$3;BpD!(%pP_C>V|6ER5|RY6rGF(}D2!t<5(+ z;7CS=-gm9u+dp|ZK6leB$AvV^l%5mvlQuaYaADKvhI5Fk}@04KTjv zoNlF^A%zpLr{~G1+E%xibm*>Q@9P3LqgSMC!L9UbGv?E);aXJ@@%NtAOGR68k`&ax zhB`9PWHP0Q*^$xGreG(ref!mIc3hhKp2NM5o-3QVG8=tYsUUTm1;V$rv%cP+9&LS` z@0R3=K6ZttD`vhg#t}s?rY=r_*j%nUOs7)=q)9ywaog9z6=FVpuBP6ZWR&4~BbiB8 zp6|9cc1a0Ezg_G73Vb^Ka>sRVX$Fii+wj3-55>rvS=Y_%5UYWXfXgAcWRaxp!=0@I z;y{zJX&XG+E-+Oyx(ps=Vww&`2Ccy(Ke5i)W;0T<%g^bh%i{+fB@37Zw<*cOj)l=H zAjG|0%z@D}AN()Q=a4_m=z|bONpKD=){5+#T`voyZ)Gg^=W1Ghc0yi)EY^)ii{GGy zqTyg`S09Kf2D&_jeec_%OJ?_Af?9rIveDtlv(7M&UbqK^vx#C>jB{&Ebf4BgoQMY( zz1O+L1DVMaSwP2~%ywUzW>?OB6l+6zI$b7Zp3I^t3|?qv`>s*oWVj~Mvm|W4OY=UB zPmV;xFm+Sm3F(bMD$4Xz-OG-&MtVoi;iRU1^uhDUs(g{ym%^cow2{;P`)S8o?fUE` zshmpr;dU*fiE};d%TtPR$`E)D3VA%v{{Dz8re7~AGw(fk0-4tN8hu&3H{TgobVn}T ze#*l`PUPb(9;)=gOuQXQX&9%i^`t|s7?*HHSP z@E)IE-rc2uD~_h)){L1-4Swa%Vleuz->Zh7f)C#aMw`i+2G-%iUg>%!=;qIPKrlf3 ze8K`PL+Ii*xVqVa@Q|S!4JmU9b zx4OyCVhFDU5{$hjltrVJZoC+Pq(m|x=lVlFwoE6WtDO<0-)4XJ19d+qDC~8rigz1z zL1GK;#o6XfXsMo4!H&qDQm$RfE6{cs~@29dPklo4D*9+NM`ToNyC@jJJ!&SPZ<9W2Zu0tNhe( zN!%|Cv$v3J)q{wObSs({e+l#0z3_-Ga6I4fA`1xULWzsoW4)gf3y3vHl>>Z%PNhh; zv9X>rMH}XTCCDkoWM~J7(!-^i_jm*wP*a8659S8ibwJraTUOU({+HOrL25Z=SMlldPpEDjzIlS&4!f5i^Gx>@_V;+7$=PTMTAo+ z*LkganI3hlZ?XHW3j}@r6GIK*YTb2RIgcWV<>)>GE(N2g4)2K+Z2vL9Cd zj!dh<1aNo=#iqY??$wu@aQLPrh627$?&`np;FvtgE1 z->*0kLadodL8`^T(RCjU2UJ9FP)0~bGErWb&zGWxX$c&~42xFSzy~tsVf(%|eqM-N zilWMiA<$L*gF6&jOJ5NXmHnq^uf6FFy6T$Y{%F&bR}adZn!?>M;*SP^S(pA$f0~Ag zNPZu@FSE&d)~6k9?>?@xX6Q1*ac*JM)~qdpL82d0le1%cFXtlhbq`+AVDdh{YbgP%y!HVlVr^bh&^gWBmEu7FaD|E zxl_CbF$j2m=f~A+*;ksrj~c#@p0@g>G@9n!t6u6Rx#{$2q8{VBfSZc`)ttGOM5*t4 zN00MeWeyYQ$A+U55EQQ-{auV`)B*-J@2f$U3fadUG0?LXhBs>>xY(*<$T*Cd-p}rg zXbzm25XID;qfNA;=z%S|9Tz57fd;5Zqj;U-Q&Pm>F<=Fhoo#X|Ue2jlYSAFoZZ(Qu z+_@zZT19_1{8C-d^&=Qf5cYLk^9do5JB?FbxFm`K#qj#G#JmVXOwbK$A~3%;)!DIP zJ}tf+zUKc*FR<&BH@^*u$sNScOoKyun&VWZ?K;+%0~=vzluu_#nC1V94DuxqHmuYQ zEHl`!9STWq}KIUh`fWfAdV*sxC40z=n88P33j2m}Nn$<7Lqyw>vRjA^_-1L&h1Z}3=ymaZt`7$cb0&EQUf$hzuXwRhMaZ}2VyZ+B zqZ)P@1hNSHtMsv1&9&UBAQuvfizV8TA>tm%ZaOHgQ)?SL^HZ}mtu$Bwo>8YJ=C*mF z)@FgL+Ixwwp=~Wc;vCRqVu2pk)L_=(r`6rT7_r{B+WEHywKfRn?I5VJ*|nO}y?vUAfX0WWU@2%i;d-XD*~+Ap z6bM=)mzq3JuF})bMJifzfD(d@gQUUqPBLFB|N#W?Txw~ z98h5{QK_f~XgOBX$T=mJpQ>?{>M&w8ACNq6bnUzlz#`d4h9F8^Wh)!^BHBgb@8c+> zi}s3e6FzOhy`coc`d$lh|Mw#CqV<#uX4(@I7)K`Icvh&iB+RHj1tS(vS?)@c5&?f; z@eV+EhSl{*iksH$y41^fdQ;H_U;oP7grvRU47V^bxDQPEgGCP5+q0llSnjFWv)-Nc;o?i zbvu+NS)B0~oKV@niq3(4%c(Ioqy|yTVu0s##;5Du6W&Wx3WXV_A<4UHQa4i) z4BJO?V6a>Atof}#0vEuVM3$$j=>Ga>9a)_q!fZpg-!yx$&;_;tD$S|m9)CbC@ zISsFWJFe$}x#zcc@{f3O+-t1y$sy(^l6iy zmc?Ro9Se(E{)dSFOO+bSX^Sx(MbP$0>UdFnq}piuBK`Xmj%u%b18GvlQU|qs<=Abq z(RzdRgY>0FmE~qPng(h8k*#Ky7*B)7G>5`zfCN)MTTh3{|8RK=sT3Y06!~rresYPv z3M9MRon34etSP`-UTWvqvoB6}vO_i`hd%rcHBanj_jHsF5n8k8otbRmTjZ_wBQKgx z|6|UqFmAqBpGPl|xz=GDA~s|+hFS+=7l|BK7IFAeV;mBm2B#x!&O1HZ%W7qCM>T#` zFjii1Coh8cRV#(5^kX5$mtMUIr?ZxlbJtb1MTt-!I|?euuXtlHHFu0lm7iyuG^#D? zeTPi9TW(YnCa)ufas0_PqfX&_kG67h{#x7D^d4A^O;dZW?;wHt)GI=zhN;^M0_pkR-PN`1({d926XN;cGV{T+Q8JTIw z(eU$5uhT=sYQ%lM)BLe4>DAsNwFIK`{S_@cN)+BdCMO#xH?`T$^CP00;_uQwOL@+~ zAr)P>x3~A124R227J?O=tn^2*8+`*|h>FHNkMofM9nV+=4};8M@MqmkVWnJOo`*yp zFv(MXzIuOuogv&!1iI4~50--)*$SbecyjfS|7dCS)H?(Sa9?HjBlmZ2{ z*ALV^vp`zxU93bwq0b{0yf`g$jGW@txM7*HbJr;$P-v?DOBYiNg>GNSHP-X#ly9@E zC2yQ7*CYIqSedHw;7$?rNa$M%CSPB53Qp`_M>_%J&B_m+EB(Sh8vI`2vyJ_nbLt+P ze_h$~4F2*1P%5_ADb-^eI3FjMD)_voLb7tu6tgnfEfc9kNMf*=l|_P-_p|=d*SqDr zWr=P}kY^K&EfN@UxuJ++y^a1!|C&7#4wLPo2Dc`>gzFgAVo|R`I-dkKorXr!RF3kq z^>P+nIJWYXjU!V2YJ<`5+Xf)U=8e@OqIPMY7)gX|3^mc2t%-mg ze(c*VwEAn;I{cK~HinQ*VR{tV2ftC{QGDrfyek2==;*s}n>ui7wBdSaVk0;snkt?7 zHXG)POn9V5!zlO}BaE`C)-It~ar&P3P9ubLut0B*ajZbZtHG|-MJW*8C9`H~q&G4{ z8jD&nzUfdTMi#H|N^4c6MAmfwctOh+9@Ztmg~MH>QF1s%1j0O6-9VV>ppNTP00MH^ zmw)Xo1tc>5e%mEdjK0QprbAk2^uA0ho51;EuKwvUt1V7zu5!rMjGUf_+WAQJPs_>=(PY}fNfN(J=3AyU{bu( z>H)O?s+m&f+-&t*JUx51Ax6^#BcV`GAI=3xBy6g2P2OPKw}o6bOK%mqZ8Slrp7CO~ z#Y*}&cLz4RWUqmyM8ozcoA(GYESYZdKLNEiqgI9Fvbp_=tqSxTznw6!+wwlzS6xjN zxUn#g3TbW#X>q9E&5WdqBHmYA^}IPxc17hIGUp3{W^H;DuMe=7?4Q;lz3BOh3PwRD z(1kmF6N3%?tK{F*ot_!1xlCz5K1qAI^23rnVOx7cAFA+VFQ` z`zJ1{&wW=taoR3#Grala>wtjs{~dV%t|zn(2~8jx4LqFL)W#bl>CRW-AbcRrI(ffq zaRDn`p64tsG)0z5!c^p&sh7dpAMY}p+MTO{f1kR~LRMt8(HEt-(X05w^ZIl^ERoqD z572h@+sk?W2tYVGU$AQ{+<)rra zkc(B_(8)s`5lt$RB%Abri>2|C!qn~Nzy~_boO+Ce0-J+`sd8^UEn%Q4ZqUB`>M#>s z0q-jyER?+<;Na>V-nn>Q1x$S-UnQbD1G{U)!dWn^p|%T|v4&$NU6%?KMFOJFK~Qxq zxlc=Nip+ExwHzzU2CPwbP+xD=efS;M9f&{F9?*hWtZF=4JsHJWe} zpTNsrjv2zD1eP+~-Rz0EWX$4mG|J(Z7ZH-P`Z37?&>TLuk*|ZCU|th{b}5Nvzg~=awup^?tg@4+4pXW6 zyEA@%HCGh-^p{NT3+{M1z5*`*sYAS{2TI&bm2$Em`T6rfYm=kZg@xezGEn?lSe(WS zI~R+UP&PB@t$%1NE*cTnJc3Mi-_DGaSv_)WmyM%x2q?MZjp1elF(4Q1UQoOgSGvZV zZ68JqFlhCZKuN8O?KK>=8P5kr2^;!hNjs5QT*{{K+2T3ylswqQ$~IwIY3f?OIrW6r zrOR~{vP0HT%kIqAd~bn^qov9D+#E;7M2AnFa4`zcdIYlK;g;smC6@k?{I|A_2$~41Os+{@L{M7LiV)u zF|3M3vGTUXXeS5i=_{vw#z@Q@A;fwUJb|>b(+_dy6w)}L@9a^E#r1v9FHv^ z4R8U**RiuEoKAmIWa}4xzTC)Y;cLBq+vkUk=4)PARHKR^r}dH7B+qjo)%z_?E;Jss z%$ua3^v@=+^p?)u6!mT{Z%G5sbhW#2*zAg_b-> z+FA#hm9d!Mdo=p2#$0afuA)<#O6JCPvtP`-x7>0osiM<@QXBx!rTuzZbO!%#j{72+Tr8xS^-tW25!q73xuhmdfARED`f+A=BA%1Y;*ctj0j=4eZKDTjds0j z$T-K@4V9gKa>m9gSeXB!LBqwMNVo9o&mp)Et)kwNF-NIBj;Z9GX&JtRtLUtNjp5A} z<%tjinl*>TOa;5md~?rg2jTE$+oL`ozO!237&?jS0-)Q@7}{JJN@PHPIsB?Jh1VL5 z5O(HuR>Sf`B6CU9o`sqZ4On5j5ytHV?wd26csm4Bqn?;Dxg)SRD5%*Dp&z_ z)QQFS7YCZ?A=JxJVxXv9kf{;uiw)7W1)K--R3NaM6e3-80hOUdc&CbGoK%%N&XJK1 z6RmJ_AQ3*(a$SufU)Y&yOkRP&d2u%I7FO(sksuYy!5gOG8tTB&TSM(8=1-aeQHt-* z5@X+-h59^fLwN~%20x>&bO%NxWP{E*MXP>l-(r<$R*9q&QesZ^Mi5Hb0wPOuQMt9~ zAI~GF3RRRM=&)%-n(IG{xMGdy)ibnz6L**QuuYBVYj(e_MCYitTeMS~-PP5?%V>b| zazPy(u&JcdU2ik{)^;3z6{&})gh7=xO7#=Jc7dfL2T~ZM!(YFO+xdE&8$+(6PN(&g zCHH-73Qr;Pj{d5Wx^C-ed~6Cw0psJ+Sal7WAoe54YA0fqTaU1ZiEf)itl6j@MIe&U zjP$1J6%|;c`C8<)U;w5|k_20Nv55@U(Dl}J5~p3odEDCBobY4#eD$a9)XDZjT9aY^kFRKxwN zP5~|b?R8N&hfY<=i)Qmbwt9*kZnYzR=M9ghOY1VUSx}8*R8w`9_6GFf5$M!RW274C zL-!MNMk*s^#vg(xqW54!U<*J@UcDAzeDZ$aC+jCk|4oINojs3)(G~Z%QJnNdQG;8- zG7O>xL`d6b*y2gqfXHj|fnBk1;q*WfkIME9z_miANN#+v3C3#~qRb*NA{r0ucRU$I zj$XW>vbh;yjnrS)jTLo96TowHXcj@HInjiHP78r!KG$|rjk3k-gPwIV@ z9!ctiM>gf4v2uO7l`j#7JM>{Ffie46iFRX=8BwunAtnkDPjHrGm>H1=h5Fj|aB^4f zM?+Oipx;jpc5x+a!p8{POd7eYvQ)pgbdv5<%ZTWU)5%CZZ=+Z>pp%zzm2~G)p5E%( z`xH(KamEx**2b+uDAYN9ku69N6y~Tl`=iPI+9(v4Io*MeU2A1cumL){ElAe7x$!m6FpzeV0a^5XoP2ryr_d#$UT z#uxp9&S(hr1&Y0OXhl!AVp2y&<1A8(9vCp)U=kW$CbK@bnaxSAC|dDE1dm`T?=(tZ zjZy-;f8MFM#tbIeLgTxUbOL`6>I@n$;u8hxuMJfub4dHs`3AlkXJ-p-ii+fIk{=;~ zmOzU5Z9OE?lAq&7(CHTu0bggdXFMd9QqC+z*dxf$UISKp3TU+|FkJ(LHoE(A=^OhF-4NA#Y~`w6uXPqOSCa zK3y*Bd8#u8=;d!w0Fq~$u>N(D{;Y@LRZ>W|KG89f2jf+>l|n|IoRzAfU1aMsDZJj# z8v(T)MiSf@Y%(Q>JG!W`dEjx&s}++j4;{m}sMoYmw{xG1Cf?3h)Zipz*P!Q9VFIGT zhW?F0GmS33#a79}!WjZP?mQ*!%V>H@(4(oKiAS*A!#>z zmvHrlLYy@uBI3b&WO7RB7Sl`~VfFom6pKV@{R*5c-~aOhU?Q$jWro3Xjg${Oi*`xSfGH>^xoT(d14BwUK4)65 zVj4pr+|oDjnhRid5Zem?h3fy|qdmoDLyf?m!F~8;m!W9SwiyzeFYRB46qwEQvCCQR zZHzsUTg&ImBL%=>n+W1Dc@2t0D$vEP=$xxEx*nYFrSrs*y*yr|V>V_ja2w*1!~*=D zeRdK{sl z7hAxMxjx;==oshJ9D!7Wk_Qsmn9x>o%nfp|TrOe+G)8^(C}C*~UxL)fTj1S4TPW^L z3Taj}+hOQ7&zTF5RLg40vrImib3_-nPePQJZYrGneVN&n@tZAT1S`mi*M?j<#QQl<8#}up4@jvZ9DfmA?xnl^+oddNOZgP z{Ql-PY6^~j&mKsk|#4x2M&Y20x0UiDBygJ zkKv~$Q+GHYXAz#w!Azz7KrdwFL12qw(SA1XuoF4!%f;YBz?+AL?g|+5J+KlBSqLYj zuGQT$0K6dgIHXftJ9e54+tm5I>d#hLjB;gha@Qr&e0%^c(w#g`)~g*X<-oG*3U^9c zij%5B(BwKFkhXHdVaP4v|1z|#i|FF+=l~Q@#6oIpe<`_=nX2iD4EE-aHyFvIT zp=Fu#>FD=%-q*U=m$uTc>ETQYl0%0f-kWiJ`pe^s)TKi3{QvOf9e!lZ|}$mIg`*_JNYek=E-B(c^8cL zmW&d!_&LaL^vM%6Y8i16RsCzUnR2>2cy{WzR_C*XD=}UiN-T$vE-lZ;g*pg&nRPgG zxH~Jr#OxL`)+chh@zY!*RKrVG2?gBy?&G?%{qZ_sB@jBqs%rwTj0};7C`e|X+{{5U z+5(YL_zsl(L^xg8E3jDY9_T!7GLR<21*R{t3q_}L2oT|Fg&H9qUay%S&(|EBHYhTr z!^ZR-hGE9z{!ZTY?h3Bx=A-Gk6I*on^*r6bf@&g@mU=~yxi|S;4r&iS(4E{h4hb8s zF`9lzZ{h_5A6B%#oF?pq;!`UyP@}=q_c_VkEqa}-2M#|&pXJ9N z^U|tbpI#(t43>yt?Ye~T)n$!fB?zBea7 z8%lf}u$Mdbep%o2;3YKr3XD{-`|D^SPQXIthQe9)q>7?aN^iSd*X4K-D;?w@dW3&o zKyIiMlCfqbs%yp?5hzydlL1+##`Hlp43}$|pe#&7fCT$eYO>(6J>!rrP^+sCo#=O|%6(GPpjZNC3X66TR7 zgC3&!&dmS`SlF4t{UcceJcpz>;*iwKX9#_F{|s|7;Ep;4(k!_Xxx~eZ+;+?Igj_u8 zwW$XU3+B(ncSiMSQlY-P^EJrd`J4y!)GWudD4qA_)za&d;zec@%DqqB^L=hB>YgEnWOk)| zu8|cRbKOO+zGE-8ou?U&iK-sy&>yqSHi^xR!)TNRqR9}w>Hx7f9~I#k9;#3F-mW-Z z65(^^Lp`$izem)yvJs>d-3(BtUEBeJ!vm(VQ_L}nl}2Fb z4gv|~E&3|nnN6Tb3^H{s{6cg zyNlwtKQ|11&{e$;rO0Rkm4F`S19kM2RzNx0*+hjgwdG`O$)BW^NEC#{dbe1>)o2=C zEwVHpCoMN>n|9^PmB65<&~n4KKJD}sB9G6ma+Vhn+ynm_9f9_#LYl|w7{=Ui)HGeo zjNvkX#1&`XX37D$BA7e9%7HIH%?3UaTXONQAreH+cq?x{TEQ_)9nIDxb_Sv;hG0?s z7{r#H+{rMl<-~eL@)w!w&G~PdRnUI`q?S3i%+~y*xUFZE2zZUelTfQIr!6@adjtV1 z{~pJx*&B4r=d=9;2?L8_Hg7)RJ!g2+9+G4inzN&rA%qoDW;|pvfx{6-^=De=FMOw{ z^pj38gr}`7ME@Fm8lBIg?607P9Gecbo|dfcwSTbgz#b-sA#9xCztHbVK|I2Yl)@$4Oyr!4){gBKA-yOhFoOO zCREYq-vnm|?_y&Acc9QW96-C!@*q0k;`;}4ZJB$E$dcBR2lh5`Dj3KY${fs;WdlTm z2{1fbXik#ibm5aWa*0rJ-T-s42FI;JAkz$hVyB)QRP93dFX-3p{)_P={~mwlzl>jj zm)ZrDS{1>Cm-IZkYS(^u>@AqLfKXQpuH-Fz~*> z-WrGn0f^O1gJZ|kz@QmBbo~KJBBFnCG5*unhoJWRT|52*kMzIXw|@k}|8n2{Jv8dg z|5x0%|7WlEI4G1#=EHx_nIzRqrGZZ%GiQbneZ-4E>J-ECyR2P!=L)UG?{WQ4Q^>#m z3UrG&d-eO?2Y}I)JDhU;HU3-L;y*_~cV+$ovnIQA{Y12r$J5@>&nWNv5}USj-(&pZ5%*sRb)*hea-MHgKn zO-VZ+7aSayJ3%%t7Z&`@T1!mym$hcv(~CKj|}r|x6`}guPoo*#Xa}do?CIt(ye*cM?-4vi23H}-#(=I z;Nm=9PExoPxFWnX$=m9Dnqm@!P+Byt~2rvB;jpmAC#DFA_fy8UAB0CojD` z9L2kT|FBT>bp(G*NeTshqmzT9QM9{S!0Y z*s9LIJ2=I+%jYz(S9u|Yt_VTKy}t+V@O~62_TP>i_~X0zKL$_rKXULfrO%*XFQ0*w zm9{wMhbx5#*oqUAaeOn>kVNzZstR5K*m~D>waOU!7{Td zR6T)@vS>ge*HSk5Yp!EFE-BUDUp>LhexGAJdK1%NHmU-yZcsW=xILC9iPi>`Wu-nk zI5xK|o!U}~KW#(y9o6Pusx-My?~<5U{uhG`K$nJZFc)ptV$NOffa@IUN60yS641F; zNUA?_aU9ey9r6*kyQI%R;)Qrlds5_cY2SbUz!ZNIzm<+Cx_zrnN8(w*!p09*$n(Td zWpCUrBjGwV(M+QyY0*xzF`>HHp8Y*bPXH4;<==fwzeOx*i3amyD`HPnRe|SV4su8N znx{NgU2ymp7q919d=y0&@;|o1P2?IOTd3rDiw<=p{_Q3B1Tg?pnzOef)1tK&_E$3ai%R^H9R%mhC)!GfxU3B8|ZiRH<9u#4@;V|!JMrQ8yjt4kxxdq zRu#|J=Xim(MVOd!$VV$Sj>3vaP2y$7vIHvFfQNRdPc-`PkA)%qqQRW58W9_wcLDZ51uG?{JNV+68gPz^vo}SCy_w1GA$uYgMiRb>|2O0M9b}>i6itilZj%DbAjBD}Ydo;VD`sL}27Jq`lv#nHj$b-QB}hDqSG@dYrVH8(wrv1xsBq5|Ps=ci3fhx7mI$FT(RJtNdB3HFA=6DE5uF*a|+?F{- z+y)|hxC}7rJY>qJ^Sysia@vlVF`#~5T0iWSK;1x%eqKj2w?kglwl$KffrL#L=Ml_0 z+VPP7(YsFojL!SCYUzGEx!=i1Hr024ORYCf4_^Pzlm3nhQ`!vpK5O8C?{i99M)j8o zi7)IYNnG`Id?)AIVJ1OO^$h#E4^*u#D-WmZa%J^fN1lov7A_iAFus)7lPHC_iVad{ zQVaX>KsLiWJQyDdz?}ledw86GF;Lr!i#5HZXKtJk7~u7Lb1VBLw|t?Zw_)0(cWPix zu=kb%{R5m%oekEo6IcKy+V9b-@6s*~s2R!d$`O=!=ZA+);HthSis4G~W#}aNG1XEetFj&DYP2r*T+%=H5YWNd=;QYF&%;TN)^S z^<~yi=G19~@fD!+6?TT4_8$6&#`_v1D$l)`)2MuAUPpd&P&Juv0>gvEz8glkyIinS zDKj*_J}7JRT*<^n6Mi_4ooc&sR7}B^TaGpAK~386FkaSiRNS#mzXck`CMXckd#t-g zJHd(ggvGcCogUa7{Q4OuhRRE^^JD{>b1!J&u)sLc)YszR>g3+sS++FdMkQ#OoTPnK z@jj3>9#7ko{?CA*!1XhU_n^YfJ(Lsc5xWde>IA)GLv7b0#OYsUCOMl3NmA*v_kfV;NP*$NSX^QYCp&?zXet zYaFGRkTFx{QdNJspQo}l%)WT=Ss-Ln=lp_uWw3frv1uA)RA80wv1oNhv3xe3F6gn@ zkx9Op;q%}S`jnjy_GOPo3WwGRXNXr(?QP+`VCMSU9E+-OsFe#HNu@Z>$$Q~iRRQtv zC`6fm+>GC3K95m8PF8xu>&c6a#``>RFN$*E$zr)j*kXNi*x_;;b#7`NaKAjYj!;&~ z`&gJXLq3O%mda(5V2BnG!&}xyyEDzB(dc|cVV>r#J=~DQT>e5_RUqlJql+zasAG1{ zU@65HewCAN#H`CvoKHQly{@q$6FsI-E9Q5mRUf2Kon5|MBbHeEtqvy|#7j{t#C-QT ziX$v-lH~jHY3C|5Sc15;ar@=9_0o>*uP)H5n7sF;_GObN6ft(=ov`IZ{)Ocu^|mWz zse#HKNaM`5O!H3mip$|QOU{ENiEl5?w)-8n2Hn=}*Jf5!M5HZ!6oI9a#XJM~-TYFz zdF8M#cNZqqI2Fm5&N|T5|Ig`(E%;sKfZzvgLEx4!?}*6cQi;+olo9cJDxyk~WhwhT z6}{psabig}jUJkwS)$-~A^x29Gxm6;O}M)oBYi6YgpMyt=1_zkI<0nT?kELDh+wUEhI7dYFA=K5U zSO_Vj`C@E{EM!>NXaol8D`a#e9J$>@M-X5R7_Sk_Ig|g~z{ZIHFu>RSy#D$SD*k_b z2!HKQw7`PPv@;0-fnxHBZlzcJXDxP$uPXTpG0JN0GDqw1cU0X?Q%zqW3GBtcpv_CmQVJ*~N{WOa-7qvrhop3abPwGiA%euvp`B6>tTb#d+^=PslNO%h>zJx&`aXA6Gc=l%x!S3iwstZE1Z>T>suRBOyuG;wHJ~XDO&6I6TkCfh4)?X#aI^-ek0FxhBUFv}}nn(FI z>0-g~t;l_&!STib4G~l3T7|93y?D(iT~>->x2OHg@BX)%2;Ri~XyK}(CaFq;FP5hn z?bsx7IWfXIj9LOxFStusrK0)TN=%}MjI-`CLJxaVYqGlEjc@ud!o$ABWP+{DH}#2> z-co`aQ&>Bc%A^iiVQG8?GDu`naAHhrae1hxozP1-a6#VFkIM}-M(lO7d-`H&8Uz3P44nJNh27G`U#)^|Y_NIgxk4M}9y2)_`AX z@r_aRU9=QdY?S+HF!~{jYv=LME^l}AqB~~i$j`1s2+poPmE}fTLN-EZI1(f3ebf+O z32G-@7W}7wB-ZLzdu2;gx#!q7)Ipyqe_Xs8=VhBL)pC)(^SR1X&5K_rJewD~%8e(( z)dn5R1vM}wGu+6npEc~nztk|eM4;WnOB;`Zeogy1e0`ce0xO$YN^^k4XZhV}-yt>) z3&@rE?)Vx`<35ic3U!%j5iZ{N?U!wm_WQGWUhTUt_B;f6=ISZRs{0FR&5nDtvWPA{z?*ctR=g<709xce%XD5(;Gek(O<{%JDI7h4o$pu->P5kkz#@p# zDxl@Pv{I3Z*b$=sH16HgGs7`*SV5prv()p8OWX_{ZG_fh6Hqp}OszjLeL0qVEV_#R zcMvSPgcc`zKJE8_H(nC;pjA`CAY~LzTsgs_zAa?Ku2oUyRU#ie0 zb>K+S8ERRn%Qs{p{5jH(&xovB8i~+q1r*Xi?osr% z`wgiu(dCAO5o?GJ?KHmKhuCy>WALj;x1HEiLOlL`rp%xly{Uc7j`pZRWf`cqK%d}Y z^X5qiUr@bqFvfM6co%Ae9l_eu-Mi0zM6OVYuQ=3n(b}QscDnLmV0Wq43OGr4Xgd<0 zVjj;*!^wPT6ufMGroiGaFh5f~=le~=Cf!JCq;zoC+_X0G`DM!R;8k{qe6IN}*dDYI zUyzypx~|5ShDk(cy)%to#`J=6wmdjx0W^8?c;z-kQ->=2hV5Q$`*Ha(ctU=N$@l~9 z+<8d)G1>i=B&p!tdu4-V7ES;9F-}WStKpyW?lFd#H`@K|rMe3Tg341?XEpkC$v58c z>Fw)U@4br_D$nyPl6jlP-Blua@r@k%e1#Jq=|6rCK^chTK=<@w(Z8=yqo~2t(#=_G zH?gd0`$NC9-(fR6k=XdAAcX?uKFPqNGXe-G@#ET@&e_hM9OFC}#C!6R3~Y>)nZoT| zHYc*w`nHRsmwo5^ckO6%*agktU8X4fPoachLyns+FhzI&=J|}rUiZ`J<91eLLURe} zsQ&Yp?yF~$aQQXD zXYJB-Hd7wVl+Uvdz196Gey}~qJZGwulD6n7(d`T4cbVIYb(R{nk=R(~wScYM*(d65 z_(6Ln%5+#_gwN|Ybw3h6N4SrLQ>*Sardm{c1X z7!|wB-K)_h@i7gYog@PPFhSY&d%7|-AQl3;aL1D`lYOH@!VYv!gNhxfA)&qp7nN9^ zE#XKf_WA-|Xg0M-*ArD zNZ%lK%&`-DZ+{(`W|d3;CI9Zxg56P&>BrMgfRX+4^SUA};e$IA+79{{?{T{LZFZiA zel>8#vs;JwDVygegj#x@f8~Elaa4eP_xCDHSS=1 zO9+0}|F{ZyzLF| zm#*~DbN8A=CAs&cvH@&YpEl||}r<65$;@*_C9X zs3`shZhADK@1#knC6JeWNC}q#xlhPDJ9os-ZQ3GNd(#9F2(p&k1!uS?zpOwFdB;|Y zruGONVDjC2QpvuWnZemX($VAsvPaRQD0sd#A*EBp$FTIcNO5J@KiJlN@58UP$o%FY zzSA>DIR@x_l?~IJzun+mMUYYyoLRT-D$(?6C6r>+K>LkcB_k)AG#hDdFtg++JeJH@ zeEZXGnNyrW(dE?_pi2{GxdqJG2-2MOhjE4+uVFo6ye$WE#1~g_o&z-6#BfoqVflzZ zqVxr#O~7z!B!|9l8pgZXnBAXc2ICx1-f|ei*@f_Dc|BpRFxK7>SLdb9k&Tt zs@<8!=*J7aAzl_#yXsdlPf95l=7lhd4N436!%RFYOCFajE8)dZEL%ime4NU|8kGSf zG5IXsZa|SQE}k-gTw8*0d`)NXwv8()wN%(t1M zj?+qcR(aAgV3Vp(3uIdO#|rh~bJc>!>4;vOXe+4eFlAB^V%Xh6Vlj~{{bjXl&DD8GTF<`$P7d~B*>@= zR+-|Akp?f}+@u$ZKdeGsSLT;`p~_im9I1cu@r8k8VY zKHxiVYJx$UUCv^_x5%CGOey>k^{QRnTyBWPI=SFAL00lx0(#H=+q58>srtq8kW~M> ztAep2-9m9%S(~(8eEZKR32^peFofD^q%DNW^PUS5MO0SWNTND>x`a&9B?rn@Lk56t~mQ8(9 z4uYG$+cAr#y2}2{>Qe4>d^Hm@cRsYEi`IN!2FHZTq=<5Q6gCp??y(7gs^_pS~F++h~+Q z1RP=|+j6$|_3jZh=V2a|j$HOyY}ge#*>iiYEe{_=e{zIw+L{N@6YRdygRZ*g3pf3e z&zPynm5HSxlOhI}Coh7{(509OYXvU9_|Hk)NRaBU_m5`qd+bi^M=Sqeg=jPB%El9H zEED2=^uM(a%-~Xa+r;kBJU5$D$5Q5fyAmHW&Nr~DcXNh%^gO5EXyV6vzz@Iu;pcFv zmSX&Zd8mnPbO4opgO`Lli0En3( zXXvLO#1GxF;y7|SEP|z<2TJm#;Of8wG~7SB4YN)DM+o(6G|12SqK0IeyGMnZe)dA#P-Ym#~k2N~&KJ{;D~msN(ud~dkh zF?w^yN_)A2N&6wW;dP+{;lbLkF}ZG0`ky8HFSV!SWd+;K5=IV;5C<&j*E*v(`4uNn z>xGJHVN8AG*}nGys@I<;6; zNf-let_xe08SUTLb+ei4`!@5M0>S$O944{mV(l^DFG_vV#;M$Z;{4>}g06$$dH)%A z^oF$q=fWWwp!?fDgwJ7LG|q?h!yiF+*kX-8>835d_VvDC%Sk^8>@A761NZF}`-yX5 zaxI5-fOF@H6uo1Paf@#jQ+Ov<h+4@Nshci5Gt)#1n&$5w+OUyU^AzHh!+I{XIh7L5l}Bv?{RZ2S#2ZF@;pF~!6K^enR<_*RW^%|*bL_1%`rcOMj~Az zu|po6-=@shVP4HJ-B26p*Iw!)U*G-%hr_rTrn{?FBAO2Tg;28mkR>FjvfreAJ) z@gnCdSk>qWuS0Q2z@w{ZusVJBRpv~Gs;p_gA&JgVL+EO4);2xEV}uWPvK&2(pCB^n zjk3b2TL3ws3#@_Yu*05VkAppUe-iy-!GDu^QsLRHw?gJ|!c}9e&t=1I%dFx0BLj%DG$u+(w~`6fMGX!=&5HJyc5Z z>hYN8JMU8h{MOgOS*SW|LXJlj_!uEfkbvFpS5oO(vym9k!Qn*Zf-KI9C);zS1D_aQ8xGPi!ZM>eYaLUnBxqrsl@YweVx#3r#*3Vo5(QQ*~ zqFqDvp&COw0Qw!(I=yKlN6fINBlRmA*`*y}T~KfeLwO>k@1GRS+qJ&kvBG{n;I&E? ziRvO9pLH}3s7q%NcqqG}CyS1uzXiynfy#)>Ew2$e2Z|=m;$#(+=CU34^{7Ag^`tW` z=DC`H_){H;HyqT}BglfEzNJeX5oD+2dz^Ce&Fn|>xezgzAwo=-uXBL7=tYNw3h2Fk zhzt?lA&cJY(pfjYqd)#4#u#^+H42iN*tti4t!$a!^MOu)J$ zs))SY2Fq!cg@s$=4GdfM@O@O1TM5n5;C`Fu7id<*FhLPX>S&L>%KDwuT_JHVZ_Ae> z<|MbxVH+0|gY{V+&PDVu`Iha+Dw?Zw7F=lhL-$D@ZD0cfby-0IPH|D1xIf&&^OgKg zqWCd(Kxa<{4QAZ;vVr*9DtpjjFrCd$;lt*1W>jT;cXY$zoR`C9w6&$d)(S4HvEOt4 z950yM@5pMrgF%|FN~_-T?_+D;xdA0rky?IoHNQg(vVX9WJmq(Wa$qDsg$Ah#M-`38Ta%O*ReiZ)m+1&@avpmNQph{i z%*S@E2#ue{^0xi{DMeX-eRFxQstD7TD(f5Jv#IT`@k{g-xb*#^cYY{3E`3d<9s#6u z$qbReY=&k{IxN4db*(Nv9L;RgViB-ZGM%(-U@&t4kJhKzL%%(E4}}_`k^DotH@3l z3H{3}w2IKfbI$AQb4_>8^dA=Etf!oQU4lAwkP8w@b+1%xF4$?UkdKq3siO5OR6f1>YIrM`Q=yV#)Qe7)I&7sVdK(tNX@E(N5Pq}@a+BFAMZB#ij z&96G2b2``SfwwybclX&b7imd&AD?N5{1${fYd<$NsteYpznQzsoPsG&Z6gTdmx*Ot z#-{2P5z6~=Q;hz%S7Cco-t*(%I}RCo8wnH;Vpi4| z$!G%Wr&U73Pgs8a(Ma~R|ABK$VU#NO+q-SN$8W+RlYMqX>2Pzmo6BvJ*RrkOd^lTb zzr?59rFYK39++;EJtiRN&VoW%<3oz+xk@}Ah#EZ-pPA$FW*vKesRb-6_b>F$3_z8-rV6G7m+c$l2A z(mE88J%~YPNkur>st&p=b=HGgd;Tuwb<5sN`7Os`Ut>!#Id(td3|Bte zDg_D^R?cT)_l(_wK>6dFM>Yj!EF;C&2~Cq%gRFY4%jnLlZj)YQUN?CspR3$mK^)+O zs_Z#+ThqzM3PAgs1$m1h&-tErv$=IJcY)HYvb|<0nY5Q3!PArd7b~E+t8Tx}=E2gH zR(02D&vxEVRwkWV{CJ?dmX-<{*o@^%LUWww7r)Qzj6^Kcw%dGlQzG@BZ?)&M zEe>`y^SFbI=r#Lz_CeSxf=$hzWF`1OVb1R`d}szseFK~OhvA=xqK7=) z@>?2B>y4NIS;@!4%VzQGj~fb{_j4Q8~+l^n|1^ytEH!`_g8F2+E7nd&*tuX3-&=+r^4#b|O(o<0*oy7A%B*>Di9 zPvw69*tFDDwCP1+8EvrAE?FJpOwjYj=h1x2Upb*h_wX!81#4X-MgqEciZB9$Pdsa{ z_mS~306ye;)wop3?<~}zPt-d9R=iw{SY#9(edk;qiE;b%3sGNw7RF76-{enKP$Q3J z*(%qJ&!$#Z>ELj59Mw&vHpRp4yU=Xm6e+3pcB`TFAZ>n$=73 zrysZ*Di^#>c6SXB-vVk%@w*PqiqaALqmOuhcz7sB5}g5{FqPX%)i1mO zcVT8uhdFe!uXO9tp*jq8u$9VP6hy2jbf6xzUtK0UtxEWyUkZKZ8x_00K6{yK&@K-Q zQeY4s2}Sm6gVW+Hg2_a~ykqCj*(}kj-eC^+rwH6rC1d1y1Pn!ZJ!!O{RV-Ni)gMcS zm;wrhSp50a|KjWf{w8?XO*mT68)PE9R>%H&Jzz6+OZMJ8&{tFr;3@*&f(nQWsnm+d z^#Un&^*WuFSmS<@VY3APXd9oY zBRH$cX_Pd_&;PZwu1dm7S`F}F)hs(Na298NzjZ0Oc2J4Ln5Fz|C%;&NGiaE6RXI0# zh^%}%6O$$@j{$*nj4Jzx>Dn>{eg1&>PQOW5i_i)J6??#976q%tns9c0*+J&9zAIs=42LhD&)Rd}S^k+w4Bry~ z9d}QcX}^8B5nn>+wVlX-V*`_>6|BiwuK(yiLXZg=G~Y) zL>2%k1LIy3;161+KINzLLNaL1{bD9a@pi32g^T4cBbk&{iqPhV# zJdAV*M349Q_<9VB-bljNc2pt!#%ulsS>==c!|grHE!VbxGd}WJX@qC4rcB!gyLoU> zso$VYU=Cvr-*(EpQe>WfIL#-$5i4RM#W8HAD74J!{;Y;SRiy=`lT%yCUMg1fg+O`+ z%Ah?1ZPm;UWg`b8g~2}Xj-=!3G=~N&1X_?fXiWkB=lnp4Vo_p}3o7^N@iF6T{i6oZ zPZ+(*AOA)Z*{OLCa;WP`HMCHn1~xji&37_Q>OA(o1C)1r4n16p>r3-msZ1qt6Hhh@ zVtXMMP7hIY8C545Np8-vRTFize@x#ch9R=-ao^lT&I$25yq3F4{Z-O4o~e5|pKBG5 z>Zg&N!Wk>8(jTk}`li5hlb(t=G2P`ITFSuOAPcn_^&u@I{RcT)k^bve9|v-n?Eja; z;4Ij%KraquF&8};$yHi|uCTkkN&3Te_0{y@MnT6_G4naPBv#`q->jZ+Hu)YDcwp9< zcnoC}0ss5&QT*!Z+zZ@`zryQ#qs|9zavrw5c( zicPf)7b`+b#IU|0t{;U^;eVQH9cr%GP9G*F%nMqf?&kUFd8NNud;e(sU@9;Ga$HbshVuUHJH(xPrel0)8f|sK|B8$dmR1A7G6^W z;`$AaQ5lAXH-7fh?5lN%%tL;`ZKm6eH_G@SG-Gth(m<*l6vH*dzDf}*O$R!l>nd6U zxH8*%S)SKc$8oBXTWl&RzFyXyfi@+P_Zw-4dLm+A9fX1sA)W`%+yUPWG%k(&!>=f9 zas*-i)c*Uky()1E990?XL&kus%7+9A7IxleoVaCphDRN9tyN)!EGRo=bK@NpvaI7361=CvyVQE%sITd|9ETiKn11w8`zML@dMkMHq1%z20}8H z9i^6D4Y++V4m?%3k)SlIe;fRGr++gSm^ModqR8NreHTN1*qJ8$?iRQLmabSXN}YGV2NpX1fnCuxJzp<)%sO46!4TFz3Nf|doZ8UOeh+$JSjgw}r6x~b z9n>Ci`V%sBpkli?#4K>woa-q!P+{sOPT)?J+5R(!Q;j zWiJS729Oy81TRAeDwT3glNR-j(K=A+~^TW+T9=h!noqrqpHtJQV9bXb#_`V)#@|LJ^p}xQsDz;Bu274sC~VMv-9mnz3xvUR{nPbqCC9A_ zmLthftM89LB-OOFDNybM3^^2(oo>Syh zr(FHOKC?2Ri?*S9r`ncl-dlhkVge2ac+(DNf(tQ3erG$GtTzdT#TUd)hwMqgH?;vH z#DwebWJ7POu)%^G>hJ(}v|&2<4ovI~g^B~i_%}UH)R18Ob1flgWXlBbHydA?2whA3^hlTcz4HqB81k_vA7DZSuS7I{Q{W0s3vV9`lz+hu^gNFK+Q-uL zJH;QN8QiYkqsXgAPa8>+^uS%hS+D$`K`tx3x{8saR z*L&G^MmpQ$h(OrHN3Yc9vwETPr*u8|Yj4XSDJ%^Cvcf z+GxzLo0`V;sr{1JEcJl0aiNDQ`dP-^a0+JyXV+u4;PF~FCQimamm6;ry5{=#sD+;P zZdXcgPS_Xf?pL@+Bfwq`t!ZV4^sq*iM4ioXSFmvl^;I|fDhTGkbz#+kTDG4N?lFXl zIEa5lGVVfXt6OUe*&YHh#Rv&&132(NM&QvCC$V68i3N1pX;1D!jlnGWo=rm^Y8Q>B zji$3&`Ya%Yf>>(|o&OGn0ydjL>_XS+4kX4I4URn{c&E%49}j&ZEe0j&#Ml8h8PKq@ z(y1tZjpGOO+tjEulhv;E39W+mrnjq5R#84s_>dEx|NAzjy25&hk#0j?F#$}KSSJ(4 z&kt@=1qfvlu>5*4V6LLiZ1PYS#MI=!QSHQ>1xI4&&Eh&dP0|n7{9%q0PIbwv*ll(u z>~dD4L{hqfeD`XJ_tgx(dxq6hAu8Ic%r3X^cj1Xq4_Rzb*I0ptM>@OByX)|IY@=j=1YB??*p`5`QU0+#!|pIK^stHY zc#6JQGbDMh;ro@{@o>>NwN-n{yGTj+Qh@@Op(DW37^xWz%pg8@r{#@QIU(21Fii?8 z4Vm0lBv2E>k)14Gq&jAuShD70#3DE9dzd>NoOf7-I+ znj4%_Hh6K~y%Sq;p%7TL$dX)vcjt~8Ipe+wJD8q7OVpV{qP6B~<(eeyaq-*74GnI{ zd8;RU6bSFMVfFS=mg2O0wfHR=1Kki){`zLFzN&`zOlxAY2DmwULKfGpEO+hzWyhTge{G9vB%|Vj0kyPJBGD} zemxSkqVjrO8<*Nsn+{YmDm+Sloy>FrA-$h-9tnp(;ih@OKuLQtB>)#IVxS1JexN%5 zyp4~XC8}MK7mg?jx$MEEwj77%?zqUpA$hS3*JbsMKS6v;cVFS3+*^>}Dx^ppb9C6(boyhEF_f(*fmqz99<3`y{PFVh4qWhB@Ay4WFj|5CgVC*DtE5QG}@jyzLU z(Amg+;qh`v`6^`wgK3wDH66r@WGq&e0C(miufw}+57Fz%jebbkymwm6ugwTekddWW zyL?lAm+P_#z?cx6C?ZEGg;+^?7XhC{kxTR z8k!4ybz7ClI9sa8z+6*Mwtg-?e~(klh5{E}fFYE6h)!3&n5=hrYcDe%3MO4n_U7&i1`B4d>0Yn$@( z8b!PEYVLicFs43(^|#MZ)~;|Co;*dGbDZZMIVck}M|}E1VIKZQM!XrnQfw+ec+vi` z@1d1O`5h|8uDoT9fM)3pdPNu(1q{;p0o`+O+L9j>Epkx*wJUg!i8@91DOhkOl9cmm z!fuw#|B=Pqh!jq)S;RAYb$q*Q&d=ueU!o9*e7i}=YSfz!-_M*Xdgj|pA<_U#LX zcCH^ly;JGNKIM;vinlHHKA9lTHVbs~;i@6nxBgVnCKJ%VwdCpxZ}%5oJtly#`s#Jo z1LqMcG*b_MzS&0%+cij|H}tuJta`Ua-T3TFKhSu2t&=#0z#IJB^ET;klX>YSJ;FSq z_Pw64v=+X!UEX+8;5|?n;*w2HsEm1G;D5qgU?;45(V)wc4(J>LyRI0gOz(7#4#W-5 zKcwIi@MH*7C(%Un&BbeRkpN8rYMe*Bzih_dc!A?*9qbMw+R?#+Ml3uM00pduw>*7BtgY9`}tccXH@FdyGT`X2+pv zJRQ!4sIKSiuW|Aqbn;}pef)uRE8#SNl;%>rt=sEEGB{LZ&^Joio>CA5dv;s4T8Bz> z7RFzrYyWF_vf%Ro@geu7qh1>I2)ns(yURZ`psBxLKc1RM|1x#Z?<`N9M)qOUiXqD! z^Sg~LG8GGS;q>Fy!xSK;Ji7aoXVdd-6xV+CoQYr+wZ4pUTNL@aLs_HV2No)DL2c_gD@vc`11Q~d4q ztb_9wb(ma7B#{q#{d@D&R}sP11SOYI?)L-CCJa_Q4ht)LA>B#5SqZ8Lm_mwa_KXIh zENMmiBZN)Wi$(AAgIH6!q{P((4kjk*48vxXRNI+GZHQ;po~CK7QEN~ywXYSw4kCt! z6?Use%fgWPgLOl6Z$XTqWFxPHP!u1#ZTo`NnnJMX1AAa)1283buxk4fbtCD>j^W2n z@>`dhjVNIx5i!RFJ((}T9-+QB=RKdkyv%4gaBwrwneG7&pfg2>-rL@w3vox>JSS~# z=~hFrTx7e;;2HL8FDO7(nfjq1I(>Ml=u7kS#_YnLJBD0MCz{#YcFYB@^J&O5z{iD1 zB#2ijq;NP#sb+Q_yW@?S?oQYZ-g*Lc)v=rCbA3JIN#24IQ^&D0{V}s=jD%T=_Rr9ObEPXe23z_fgCgu3jeF#HmS0(`u zt_h%jrOJ&4I+;uQJ)|EyQvMJ1TEJf=1Xrs^j1(hzw5xhCK)IB5R!cUCTLPurO|v&l zk^w*){v~tJKrLtDeEr{KgUHX09^Cb)Zt7SQfLplDvV5miK1I59Su|AjAKqs-UMldB zNdP>w-oLA^$zza|e|VGssr(~*4g9B@t#P^Mq=1NI-Vck(7htgcOV7|4%^Zwk!bth7 z(3P+Fqj|~)H&K;w6XAc*?*IHj8(-#8gC%i?e2T6*C|5y^R!c1YZ#vSzX>wrT@ptQ= zqh>9?q)pDF_c#t5N0>)$>zS(L0fpIrk@^CkBmebOq;VM!VgOZd#8Rz7>S06T-CKC2 zm{hB*81OXl8ln>;0|-lBDGZqX9}jByt8P7)AflZK=*hHoUUl@wzD2E=omsB@I2-on z9c$&v1oM0Scj?nYmA@6F{$_Ce=gPj?p!B@D=y#KPmJUF}SoQ(xzP^=@|K3tnBGbfm z3XQy^F%|>vKCVXD|NbC+vOu#~??+lWzt~>ZKpLmZ8fdPZ|3D6>sGp?{%t$N$Z~EHq5C8hwyMMk`i#)YiHO%3E zGJ^ID>tOte{A8S1fg3K1#D?VmvGAvXX7S$d)VE$M01JP{BgaViX8O_6r+MAA&UJmW zi#wsh09IR4oDC+bp^4|T=GckYX`guiLErxK2NMaZ>9x$Ch30!Mz~1~-W$WQXvb6g^ z6%6xuo9B7)Z+%)k}i|ufvRswN@W3laC z|82uwa|Wu%dDC%9Gg8DgxW(1m7V`YH%SI$tF|AH+ z0pyw68tS&5oFktne`cZ?lh>!!TMk38m`AfPTWawOSsWU7{-pV;;No82uy;f07dO;= z*-M(1#MP*B{DZysf4P9KI%ukj2Pq}4g&r~!iL%Gec`JWO5kc4cqYnouDGG_Ba#PJ@HD%7HEhyPxr?+QCxb%tE0zQ)_(5IZ3~zp^ z?j+W71wYpWLcY^KlEiOv7UtcT9IrN@hwD?7UESegzMMq|V(RA^7m3}uh05NQJ5#lJ zV+yhG%{LzDtODReCY2d=;k@kA-D&`KpcHeyvS`@eFp)#x)@5 zsMFXy`Cq&0ME5n)d5DiDa?1_SUxq|P*rF7I{u19SBaVtn?)F`vI_wR?z>+51{XYW48OYNtQuOIL*zv?{zx|>2Y_YWZ%P%!`Y%W;^f4i`)~+maw- zi#N~XBan)=rBbI?uo$DueJ44bf`?V_j>w-M-gmt@TMnb#z#LA~cPdPG^l2b0k~N7; zm5aovKI^MmbQI2!%flR0qxs zrfhU2;8diiE>#2`i!x9d=-=1AojEsFcv1cDCIYgBj(>=Fi15O_RZH(!)qi|z1}27E zpV4!e?4e!wF#=H5);g02aq?b$g%WeBjDAHwHO%{IC9wQ<@bb}!nBCt5GGbprK!Z#Xuzx%4th2!rPFS=j8`yVUDOcfRA z77DhAi@*Kh(T&9|OtZ~}WE`}e!Q8iouw!y*!a;$)+)4OBM*=BIQKy5nxoPXd|J;5m zUa$OIbs++8`TG92Lu;{qIzM%#m6=a@MTrV{#RurJtt8EN6M`Iw=@g}&FuXmQ_2@5i zTsqjAyhpEq+@Ehmlru^aj=zQDlA>MT0Kg)8LF=pk!!*94p^eBMq{yTLcW|5^HcU_- zV=3$AE{+@K|HkkZo^g9vo@It88>n;}DnAa3T&(ghA2mTgCTEmh6H$;OW}q|2fg~P> zLE`14(-}0PK~lI{@Ipe{4vFDx>E4iGiOLcb1OZqHZ^ZO$r|Ma25Am+x zNhBfvD_vYatTwFOKYp;oDQf8?Mp}ShckIpe`haqr4t9H;c3X6p1EN)5#2m>K*ujyW zdZ;`kuxN|FvhpV6&_wv2@1CrukQh4gNmdH>Y}3!y_ALl`=hZuJ_~x1rmgdZ6@!GoX zxU67xnQQw;J%u!osr^##7W||1lGl#^k6|AffwM@i@%?8}|Eb!2k~b>f0!BBn3B&tI z`V0ZR@qlifoY)4dUgaN1vWV>-+Nf7eZ z0JP$iixbIz(3LeIi1_V5F<<3g4!k4nXcfBVB{JbS{3qq^By0YQ_aEup_aKW-eNUWq*8f~oACAmEr8At_dc}_zQ(N2KG<@I2 zLOZ7Z{TB!Ch*S#su<>x*ItQX51tstoF8(t^|1*fQRXfi5#KZ;PS*5$P>%LG+&qmDov3U-`y>R|2*FQ#X4U(MK6JInz-6WzWdG}v46i-XldY@?g5S6 zK@+A<)dT<3E9>W)Hr}p;aM-WJE1kv@yz7Zq7TQdHJ?iVRK$Nk^_$|E9%AY&#HT@sO zUY@?>YKzz%ZOTdARf5lTb+|9yQ(XwL^+z0z*94oM4-9$MUm4v@)P+SgNXaMiH+~D@ zL?x?V^wC%2-|ZhA__#TjzI$*uE!C;yAufD>Y;z$P&^%J8&r~qy*eztW^H`Zua&{b6 zq4dGUaQG-Pe{gxjUcWWHKh}dM^?1iU#bp|}DBY?r-%xz}vpsw$MnBN;WT$7J*KjkR zSDE;&k6~u=c?rSzStc}hX4*2f&Qs}sM!Wd*4?T=fQ~&8Pv^~7019yymX77`amWEH5 zslN^|nhG-<&@T=bS(#_RP9aSjoPLoQm^xk}Y`=-jrb)#``M)XG;eY2vTfj$?U??NP zz1CKPSFGfWb9mjZUTb#$wBLVGX*L};c;ir?A3@_d9wBg1Z^%;!*@Gw-G9bLb6T`>d zt7_(O3?{Nx3-%h9HsT||@_L1ez@NxWO2LDQDXt64(ITabR80v3?xj@6^6pLuFX!2T zNiWR=iGZ7(bY4W9pwq`N??bENkobZID+T(pkGhzb?dW_&(y21R#1rH$u5YBjJ_*b+ z3@~!3eg-BH;!tnp1h<)K*RIgOy3*^5GzP!gXUu$Ra#&>Bojr||UxQ39dyW^qR}`3Y z3(E+tQUz%u=6UQaU_Orb2C8==n0dJR(}CCrB2Zx( zHp1|^$w&-NeTnr`k2}H7pzTw1wBqOTiwx!|h5^}TDzF1;xpa3DW=K~-|B?6ai1xCQ zSiEx_?E$>+-t^pMHQB!hkt_-;2vD~7oE!>j%D>!O!p^=CK zBR$)u$pQKheSwH2y<7Qg?XU41BOmwSXC1p%7tM=U3B@Xvsjca|Y*{ICJe=g26G7#`!FoBM*Y<{BMU+xV-}izPUSUa$i-b9k|~Efl+FO&ag? zNk)?J4v3vqIfZRz+Ai-9F7v+(N#wzh+W(g!spQN|tIDOsr64+e3QS_YkncdVDT5?2 zOYpBR4j3h*qutR|Ygdr-V-7T+*x-Yi_1w^}RXf_2G8AWrIe=t4G|5sBj?W^(Ri9=0 zswYB4q}uVFp#-K*sWnj-*B|3h9KqrxySzte2Qq3ujF2Js^cUES-INRhlWk)lew2X6<@OgH=&T?$~+(zRkf}sxI`l=SLHf6u;XslPXWA}y0Ay*kK zn_Mi++v&&v5nhIEg|2CLx6>fM!c*fKzYgqm^I{Ies+F7={qO9Wb}`-`zus7@;m61{ zMUo4utQp3-CsYtX8SpFG&4A&?y<~&m2i?FT)kca1Od6H-T=wdQj2X^x?azy|ejdL- zMrop!`(b~;=CQm!sn^khB{vn#6F~ayaI7p<%L2BHt@5+zegiXub@r?SIwW{OR+z_^ zsImdo^gm8%>5n}as$&qdF{O1Jr=CEN(}h*+n%zM^uYI`(zZCND#~WY7OU`A~kV^^d zP18!5zUtVf_qsp^e>CdgO5n8-*58c6X{tgK#Hb(;#Zdbmz=tW#YVR1RF3^x|I`|kG z{|6WR>o^ioqoseqfKqWxv2Xb)XPhPC<#>^Ia*uFup{cH^qfs9v)Z{vmhoWGV5-JCVB$Z&f=`o4u2B@E;YDyAdgYTKOkM?rwKk8%_@F2WdAP`_~`; zk4RD!C4ySC!IEw6|20^nP_$>Hu-DkIGTOjg-l6-l{(@GaT4KmA7@eu8=G(6h{S03s zynK~nn=qbe)s}u7!KX%@n8SOp@^Q7$j&F^RqPfVqfrc?7{KUSb4^(h5f^M16^d)tRYR9@tsWHxBZ)WZN(b1zU~dnj;Qw&8(P~Vd91K$m}PStR%1um#A{Rx(%cj3hj zIWi4UT-{wQMy3lTslu!1+_9}7T>6>yjoL-FM&$@ zBv%ea0eMgDGYlscd=d+5%Bl)0#j2n|jZMtNI(%Ye;q` zD)8lLuq5~ZE6GDe-oN&UWaBNF(OccG{3%N!0yh5m_ze#)eU4XkaoT>e<1$0P%8@NI z`VOoQ;LVX`68C+*PcbP8uorx6!4YBcVOb1)1@$Lft$?Ggj9qwVhAWb_Oa&VRQuW*e zENGw;p69IZ)I?0%^}_!<|JCp+bE>W0JAOCKR}U$9{ZJmj8m!34(@A-GF!o`7KPO|^ zbvI0&5((EL2XNCV-m3*_z$_M$g@{Vb8e;V=1grppnC4&}&n9#NzXdEs*$huZ zqQ3JiEjr|AyX5yXdQttG@~Ezk4AQb087PGEBFdOi z?#cT5)MvN$HOWpKj6cco{(-a(7;LE&l4`TgKIQetB)#ZYSUJF>4bHL=!;(CC*MDcH$h==Y(oGij!Ck4U;S*sZggPBr95 zlL_EiDb)(#>lZ@V7edH&DtQ_QoQto}1f()F+uX3lQ}A*#w3B}gpM2zUdTh$6xOQbo zQ501gAq0CR&UxK$n_L(9#_AEM297BxY4q;**Zz{+^@w6=r>BKyoV#*Vfw5O_9)vA$ zXz({KLhrT*0C6PoF~FeL`h3|Ih9Vn5R|!N4Q}9gbY8$;V@7jwJrSo7(3I1@st4E-V zn5k%uy}C|RbY*d(z|xzWP+PpvcPkYyWtqXcSThCnEJ+!Fk7PQ80)j5zaoS#|`XDFnK4Lqx3M0C&NvztRbkEzzUxW7SY32=Jyk zi!erCb7BU}F*qaF_#!7y&$g`vG$~Lg+tKKkr!dMIaw#5M@F^#Rl0TRv0cbF+;;4Ym zuRh$l^M$J3@99lvR_WfYsTL08AN80X$>w}I^AMP0UnQG$L}^e@6!0m=K)0*>M6kaA z60~l5&gVCdm7D6$08r2g4+K|wLJkoI1L3%RENcD{8RU2dfgLp2cV{AaCu$v;+XD_1 zd?TN5!5|_Jt;)wz99`VORtvJuHd zTnRqA1!>O-L6a=DW2>2K^@zG^CLMTNkb@^rZz|CH{!CKk3f!?k?7lr7ON_dE{Vn0E}h!$1*rYX4pqA@Evb`6(>% zSV-H^9x=|H-#=g=E0Nj8GQ`6in1d0C3y4L0T>|i`c07>|UlX%TyRRO=Hw!H0f#{Rf zxBHRFd>lNh{lQD9M`SOqZO#zcW8o1`EUlymE!7i2ZDoH)Nc+Dbq?ErRB%s^aYco>C zX<_>{eP&RoVYW2f(pAe9AP+D-Uiu+JDuvum-duS=UZxCb`Y_bb0(U&{ zaeja`a_LjeAutZinfI)yVz%urH0mT(ZY|DoTP_946fc)Ptb2Fino2kyWInfK)QoM1 zCFgLugDn0qgg~p6TmqU%NU~m(bBXQFCJ0!2D_?%=xe44HRhwX>mjvpEh2L`2PMpew z)EU9D$}%1Ur4B-C16YT_INRfnR*B2B4+OxnoYpW|f=&L(PAOTIMCShNHxLu5=kxc9 zcr@)|AMq`Hti)QI1bu~;^p9@_s(RF^NHC4O!K$F}-|<6$m1TC{OUvbaK;i(G6;_s6 z8(=}6rR+hw7}@FsYWZox(!b@T41Dfl1J?N4QuEICM?dYgjp02Wo}@Vg24S>6=LCO0 zGJ0u&rMO3OU-e&nz`2sq7eI}dlXu-eS{v&d75eCaz9oGYu$hMxBg+X1tKzi))AU-` z(;#C$qT=#p#~tA9N6Ean>UjoI$);%ohPItnDrCE8G>2Na3(y1W9e_=+xsW_J@Td>i$<6k;mQ3J2a#fgP1_YUP zpnh=fMqdle7m!cSQk9#jaOyDU>W~Q2&-3p`eqKiRv5Hw3thOKe&61tv+I@9WKT8by zJ3iSuHCdBdiBCSb?fHZgT!y_Q37GTgB-a>!QO2cyrqB}3^^auz@HSar|205N+!-Mq z8;FzNh1)Y57JUlP#lG_pt0-5Nm=@q5v>WkPhM2TtJUr)1@E@{3h=F=lR`Uq+1@+9N z1En5;6a)p7=b_u=>z}b#BZvw}`++u!ftCjaTe15=p@B!Ba61lI@YfjRg5dyp zJx;r9g-0u0^+^M&{M$r>@VL*u7u^6QB`rIPuP(odBlRc$i5u4QrL3U0>bg*`%oEDv+)-l|BSKFSDHSX^f@R&HSA*P9rZ}m(v7E!}Mtkdp+_U zAzobx^LZbCpz??#uXLL0eUDC=uTqTh2W7&1wRwDW$K&_O@RGqJvS1E5rPpDi7&tZ3 zCBq&YdHdl87O#>%VICS--r4#>pb!a^PiqHZ=y>1ZK!ueh8o+XdL>^ZUoHwc;u3x!2 z#VRwYG<|#W*t4X?hB_?h6MZTJ+MYm3H10&{!G5H&HiPn0hjHw?wYYX1l1r)8jMnmm zAZYQmcGIEO)Q?Wy*R~^i{KJ$@^f(=dZ`#9Sn2b}_HYYHZ5cGd@&1b*4=DWYTrg>%v zun4<9y(A|_2Q8F%+gkx3g|VjalV;pVmHT#jmDllCqf#xtZm-tGV-ts&u-8Kxl8qgN zxrT!}&M(XUwFC21H9!?O5rQ<3PKkx57tB!u~3;sA&)=+j#nOH7hdeD zHZR81q%xeFevzXRW@h{0GtGxx<8*{I(Z z3>xykc%MCwQ4a*7?N!4Q2^V9LhM7@a9g?1LXJxwO1bD>R+Uc` zBOl=NjTrJdfHScZ2~8G%CG(suJ$HDn3|R?K1q{kAX)b^-71#{U&oY2z4NI+JtjK3x zkChD!#Sc9A#Ij=~R?-+N|J3vEn9%PQ6Y2tIe*eOR&H%7+n%AI?&K25Kc|_;&iQ-*f zeTOtJFhiJFOZRb?h!V0jlO1hL_*J_-ITqmT6d9VYHuj7_Xf~pWR)JGzYOisYGNE0T ziS|>iYNXei4XDtz9yJIhq3DVM<698N8e(ZF++c9 zv$l)|1|I@#rPn!$#RCol{i$zm<{UxO5DIEUf71D+$>4xso4K+gZ?RTc&Fow*hWx(= zuzqj0(3ibs@<#z6#Qo0DUVk)p!6eNYKVbzv(}qsl;rk*QAr-0QsB6H%M4Zr^fdAX~ z%;1(O;y~n*>0lkD@Ks$i^4ZSD!pEc7A_EPi(ab=7N^Fxl-`UtE)s8E0;&Sk9dbMOX zUWcq*svVoYXSL^>gEp<3#2LOtyYJ)E6pt;&J36TRvQYgb4%45Sy4QD(XRkyZO>N)P zH9Ae%EV$qJ$|hddxgqCH(lh|c$oLMTiTIH131r*y9eEQ9<=iBXvm>4pu%9MSf)X9t zb8@O542I9`f{k-{Su;kx?_g2QGR;U0^OC3upEEB!QQOBw^_;-7jIrG&QLi=&lk|Rj z?YB%upoPD6j=`%GkbZ}^X{`2rBp!C)2vd4iT7CP}x5bkJ9RLS<*-a?X%5<%KO>>YH z*gWid5}sgPyZOG`dg^S#RDW(kPe}H{I3dd~MseU21FMdzZes1bJ?Y_gK5OHvB9mD~ z$!f_2JMV9Fe$QXSl;7Xm0v>Zy)=uqq_vN?pLiES4mbbHFqyoCKjd_mZ&6)`$l#7<` z7yaQ=l$_e>25yk+m67lR@lF`l*P%(l=WCy(eYk&g)+dg`$s9=etg_s0S{2e}x+>3( zRAoBBn6k-w^VMhPMrmA={+zp;M268y@*7?aeyRLNG1Ux$*Rt*D@|bqAljO(qOQ2Z~ zD`s${V`dG&Fk0;Fs7MKJ5VERFxQ$#|K6b0kPK9M7FY|tc zN(=jph&k@pCPEwqq4(&m@UbiA^1qs>e*aJhXlWcCbZr8X0(`Xbut6IXBPgcS$0Hp# z!|-QUgTYpvj)iU*+z|?inU@&P9A5K_Or5j2eE6IY$4}DxXaxJ~n_E&Q)3@R({=hkO zFjf&~pdg?|9t%TfUSZU52|aaMYF3Upz=y?)ZcZ|HND z@VHWwjgUFUNu}3%y6&A(e=2>5uo`SOkR+?L6|>uJ8o9cg(K zqqT;cefo%2yOBPvD5CUsn!h$_d58YCUz3w-7h@;mWF^9D3N=6Yl8-3)74cycofsoP z>%c>hUtle0>5cb?XUdSR)Gau0gJT+ztMuNZlpoEv!@-iPFTSU;05#P_Qh#H{|9bxJ zdy9%>4gMV_my;lj%ylN+9M0$ds$|ApMyRm5^V?JL-WjVH(H0V0FlU+0u8({wCwH z_bwhRxCZ|f6MNF{i?W+|F29Rq5>gv zS6TSq{~327c;juT0N8e-0U)gi*Xa-f(sl>8AoJfkfd1+YGCFX`z!~dor z2FQ2(OF>K&xcvX7XLft}-}KC`)9cH;Dzi?PPEkcf@$bJDc~FW)_z35jVkx0-qy8KH z4C=X0)C{TASqVX(^71BMtEmNHC2%oNGR#rlAd@eQ7G~XDb@qC%`Z4F{Yo7~Tq*m9S zq%EY_QLEzk2X;B|u_gTX;#L4$!-k+@gJAa0{R2PpN)$cD27TVP)&8Gl)gD3bJOa0D z>I1*|7^=zg2uwY(TBrM85X4_K;usL<3=qGOcHlQK;_CMdkXJ>$&dvXJtG_Jw?H?5v z?X=sAd`;LNFix{t#ER2dQ{Byp4i`Ys{3eo@U z1{i55z#`Tp0KXxl`Om`trN;N4h5wJm|Gy43&eBk|TB}rx*(zzrok^;TbRgDzi$@ z@$L*wXZr8nefIff@I%^_Af{Nuo2!$-h<@Ybe}*9Hm$$(o^^f3idnp_u7Va#u;D_xw zn$-tNHKVAvN;o4#2v>%cL+piU%ucZbOrP|?*)zx`m#23%+gQ;%PNkD?^{yv91E~Ur z{nOnd>hibc+l6rA-wec1Kmat_)lu=r#Pkxmc)Q*QDR5NS49P%{*e$UA-9oTPx_Cw zsq_y7n&7`xw&L+EburoMZlc9DrnVN!gwYpugYC)JsxY5br@HY9ot}01U$oYpA#qQp z%gw&5P<*}^uCSaj{I%51L>eDs@O@-k1oIBgU!uVu=W$?~+hB|b1nt{k95vI@dWj*N z{A&1xSqkNaDKrPgnd-Cp=WU0dQL3WqpO$@>X=a2dxJQ*(aLZ#}K4w8|-;9*V;CP(9 z!_FU4%BMpoe*E8Lp)QTz@(;xy`NviRn!p3%3>4RGlHsOGe&+^HWbLMrXi9>1`$Aq) zo^*)0<>kzpOIk(v5(kRf>hG!*g{Zln#{XlnywDHs-sa6iwBPe)+$<58OQmLd*KF)< zYi#*~o~cY0D~40^rNuXWUi{Yv{Myo)&I@p zr+3|N_ErCU#uT1@R`lg<3{%wxUU0-Emi#NX5N8!aQb(O}32Ty>>^ z|921jYu2UI!M`;^o_}8q6%1l1B?EnK9sJGo0@0W3AS5-++JjC}7Q*4kor^v8fU99X zs(wX_v)npxSWF)CmJjDBDR(Vanw9( z(B`L+3-{D~T{94o3v8@tyOm_bW{hYl*5n9^$Sbw1RfW5po(&Mxjnhfm~x@Kl%2%O&&r^C>U7( z`QQf+e>3>Ie;WK_@Z!FgPh#t{+uRmOfz_8@+~OOTw@~>ESiXW(ikdLXf7*2r@o#98 z_s@$Ef!C6oUUz54Es;}P5|p2%xmoOKu@VSyA?%{+(+QsFGw_Wn9jCDsAAenKS{eR) zvDNesz~=2HavO^0Ui}fdPW&-lAg`>3_I;!&(+NsAuQvDbk(U{rVnP%{7D9iwl}C!d z0a~>`-iEUnlwrm1S*e|?r7FVRg7t-S8`ek2N61rpB8B6#uBpM!%d@0p3HS0S|3H&) z8Rn*;@?$}PYds-Mq5af8_BX}le7d}rBwp_Gy#3?GIk@c&H%ql zv43a>d9~Mz_D@GXK>cm`9{#y}kHHC={I+7@K;Ak_^Ge-J-`S6ZvoR_Y>E3#8E@GeZ zA2HF}g6=k^H2pgwdUc|`W;ufDQhDz@EwNjdKZ7^k^f3a;m%>v3FG2mt?p&>^lo zJ)3r2)gvaq>YZm#Bcr@tR4GG)R-wF!U=Snby@|i>(0>2C!vEXNqW*ESb(D~VNLsA& zFKoxAp@}=1RtpVc7tnO4G-~lruK`M;x)ak>&(84F8o1~D^~J90E8I!(`V&*~!QH8u z#&7$AWzBp)098sPiSX`E7Y>hE0`127^XcsasYc2A!5ictgvDVyZO2lfaDIoZ7-GE_ z7C(CKFb}~k|8l}iK+TG|r{U~CriOP2MOcx$iQM^2Rbn&(W>l>SdejkLWx@2M5*|zK z;)C{ya3Gh{PiOZ#uIm@2DQcK%AE`8@5BhlFg(RNfVI-C|9~)mIepj%uo{HK^=emhX z5f6Pt|F(O2^s|H3>f6nZ%%4lE13i^aG>;ljdeO>Sk_mwmw=V)UeTl^8wM+X|FZ? z=E!Mxnq{54u8C7SJ4*t0Fj`OS?$>Rorm@RF>(!6Ikwn~qqjS8L))56E*iq+~3GHaf zbI{O9>A~!&4HIN^jK?$r=UDalP*r;^DMgdt__s%VkEaKPTpmp@O;xxjzw8tIzV-Py zo4Cr)X&4Yh>8k*wNMGSGX>flOF6#5RGh*Bq!#d|G4d8;6R33Wcn|sayiuZn|SH0H0 zaLm?pQ8Fc&SVGuO2qNmsISzdtY==-Ux)mbGXv9xi?I>ptrmQt`HkW(D5D1U2-<4`@ z_dhB`N?7V#mCCIZO;X8B>_#cI$Ryl+3&xJeQ)d!>Hp3d9ruII~REsZ+v(380 zncMnC0gtsjoyY6M*x2AR`~D`QZn&VUVmZx`9H5^g zIcUtC==QC5c@>Ao2GP7T=-#01Qr{r)olNPi(UB0pR&3+-m4qpeUTn;m@%*JnC$w{= zt!zj@olbk@*p7=pZh$9n<)~qhhtjSj6L`bka{mf9fhoA3qYnfS9{>Tz9jYzEntd{aRfPLuxo((@Q#C_x~i)CS?w! zu-2Xt_AHoU6gb(5X2K+j<)A$-%dcM*_l@gPV6x{bc=MWWUcFs|;!yv<>B(vu(G5rP zV_bgLUN9B&^&HF5d7EG0=G^!0dx3;F{FVU|g4_;;_is$JzITcYnY!;a53j_a*=hOl zR%~1UkZw;Gl4I&hU8hJftKIDoyxEP`QQhUTjwpWHhh^yaBtDV8#Gn)Jo9p|XDUT*p z%r2mBZP3jS5`|87#~}BL2BdH{gSFlWaVaRH-S54pV;L!5To9!%j??{vs{7fN-4?Ka<*wfg%(;hSq`m>@(PVew4mOC*^x)hg%n;uCjm-GqthTU7fm-~pi8lyr~b zdm8>+AEa^1Mk%GHYVJP-@6}co{xq9{^dl=8uH?`5E~ab@+OR=Wb5`T&M>aMpUY)+H z=igl*hjtKf+Pl_u#;twbO1ngd>(i9L_I+VoCr8`4k-EXGBTel5>z`YRJf02JI`cc; zMt^bvj26Ulo)rF^j@FoA31s&R+?}cHHB{B6$)70r{9O{vXkGtYx^*!p<*3%}47>XD zAYXPtXZBQ-gr8wbx%&ye+h+p+H?I(H$);|^*)XFA9O9VAMiI??;W9E-fkKT3=r|1y zW4_aJK+1&ciSCGrhkRAtinwm!5W(G#?fKw&npHY-6Dwq0w4Vgk4Q^4YcY8invn{L+ zunRPGh2GKAJ1h+B9&-R49=C@ySl_S^7+bPachBK2`#%hNh);j6qx$B3TC^><`&)lZ z9K>k$>n=Zm?^07yjN&A<3XW`?G?15GzNpx%z1osr@xT12?Z>FT-X9cVD{g!)4yY2n zE2_3v|!+&!i$U3#@Vcj*aq6R1~x{9JIcPQh28M9a4~X^>;4RB*U(b9$?UqQCV_tL7(} za(#{S?oAmW`Mx|>D!TkN8@!|OO<$bg7`>SDJ-6lbQyijpBOlo0a)f~V>$qo(A2ZV~ zMj*}3#~XY$;mQhp;$gwGXbSrybQ zV}MR1nE}r|QI;cDF6T8a2zYB;S|=}O!Q)2EPO@z>j;|z?%-#Nl-0OOU$1LxkE!-e{ zcgCTIVWiwKt$6t>sUWr=J*2@u8q)=5LJO&xlJw%ByMRcMTB&|+@vrrYj7H0wLakaA zl>$vup6+{{6l}DwV-WK*MEEn~#X8pWk6G)|Q{0Aw9hE;BN_19hpz;=|l@5nSw}%kP z>@T&y&~_8&QxT?jyqvyz&g{{j)EKtjHa4MYt-5_$KrdM9^a6e|G3cLOUTGl3w#F7j zGHbWhK&VtER@`Bm7u_@2wIMBFzsE%3c3R4}GXh$OejUr?^3LsG)4sacT@nf2PkV5} zm$x%^vC$FsWOdP3X@CiDT3BF0uWw0M^g&MLVneX)qW6B;w-LKbVV4Egd_FcMg5eQm z(6O|Fz%Vmpk)*=o>h9`C#enH!09Gb#GivfeWLC_=jCHP?Lg~djKAe;%pGc|WMx%+D zx%75ex{fKb*amg%4Vs9AUM{&5_0g#g6d=43ZUW6*%aUqQ>y9TbL58-vk6l%!LmLE3 zCn$c%&xCNSjkm5QDf6i7U z5pNaQxSq{xH;V^X77D`1ewhm^!>So3q-)36M0Q1iN={P>*_>wgO_iK z08s7{@_VIPrU(aQ79;5hc^Y4lsb--xwF9MC+4qtcKA9~u97X7LWNZrGgp&*8z%YoM zgM;Tp^!+fSm#O$3;fCu}S`{98+Gn`&Th`Rpo{&n6W%^=D#nRxPqv+J=cUmhNO!2M1 zI5-v~W+2wPIP&D$yBra|QpDps#UGh>*uMT8ZdP!y-&s9xbKD-Qs>w<&P!aD6uh-G7 zlLVxwqgT$1)8(5lb}PU3lsw2r7k=r}Gv_o%<@D3EFga=_1fT6&efo1GXEpp9PuqbP zevbQO71nU2l!BQh7C(@>vXx&ndbRAFw(=B15^~Zh86oxSdf^>P)wO4Ton3h?9PBXI z&~L7jr6^+}93?}KT^=)i1UoObo|Y~6eACA%A1_ZX&;^KG5<0oJ2HaQLhq;aD#S8zs?Dn2%*)1p>Nn@XKvE6ww z#nflNGs_ZAIBky8*2J1B(DUFOQ}Qw(ByF%cn^QYSR)y{xAq3??xS57c99mUM$C~qH z#toCwW7r7rW>BFcZl;i9vE3oJbpC)!#SUu3Ca>@l`7D+8n3lg zSjm>=xlikCc3EZ{=TU?yG(K1)Z+Gwc?$o+YOfXxcnW!WT1q%ddjz6uOvn>m-kzq6| zxc3rGBvfEZ)UvD z#S^N|TK3p7T02Q&(I+YAOK*Pus-58Y2HZP~ZmOY_3@bpo*0A=%&wti~}GrBfx+iaZ~C5Am5g_Ue8~la`?eJmhr7DxkD3U8emjn|ktL8ahh!(FdZ?c-JK_5c|B% zBd%m{suN5J;%0=GR(8@p1XDWxblqgL12s1&eCO$V!+*>+y#KAzbr9|DGTFe$DcOZV zr)T4bTG=6X%@NOeYV%RWKqcji>pB9w$Xc#+zw~@p1+}3YHRofbr(sjAxf~ugS%}>n zRD#5LBME!WO)dU&)UOMR%C!2`5H@4!&f}AARDN%hCFbar8F&NF*Gt_kjWt*8ETq%! zFPU4p_P6Od6&@$f7YLe&9?5h!CZ21!i)ntN$R);Lx{X2vkt~X6GbeKA?$K&g2wN1V zyaHZlFdUOe%hn$d*3`Ir2v1(R@Dc>2{i5l&u36!{y6i0;UMq6#`}9p&Ta~I#_Mqdf zcKyN8U6R9V{^qK3jB4eiG!>OSGveBt=_AMBS3Ao_m&_xcb4-o2tGFWaiAaag+404U zN5Ly|&ktI~##TM*;T~BA+g^3UoIoMaY!tae zmPb%6acj2l84s%`-rj`n648@6#}8|XyiR9$NU|%NGe`Kr$427GpILn>`@98Ypx`wB zJtwS`${PW5!-jcGmTGn$=bhov2IjP$NCS7l`-XBB!&x@Fh)_$h_w z$DX7nW9|kgh^MPznR!-2<7oLh<$0fT}>JT!X8(@+`qP}Xtpe<9Fu(>C6CDon;>HT!fu zCH)zs32_ea81YKBYs=rPh#7QEe=FPk?Ar1Po6&KU@N1gOCyTis?-n0;>-z;7r*RkW zrawrUJW5345@+WdmX z>otw)<)Vt*qF0{p%Pm-!)13!qE~^fH+=GT8GP3+x*o1CEu|eg#8rD+{2F8ip?g1y) zol^_nsZg@S3O*R7WZxyGxeyu_{#Mb3<0h}|0^^HD0pFV!aN13S8qWa_bFv`kkX~T* z^N*q{*2prdJMthu`ipgAr!0>kpHa;7r!Rrg1+RlO^WrX>PFi|&zrZ)enBXX$1(z8M z&^ekx3r5&L#EktWq1=bfABMy0-rB>5HRekdu>NHQi0O5@!y3uK&tEU&l z$HDZrHLv-y&hakNOVhP~Y`zDVsYBcD3O7v(W+~-@`b+<^iheX?m)LrwY_OivHvo_4 zA3?+SfHYzu}O$hC+S36_UKiuTXvY++5bYnaa}>W^CK2Gth# zd?(;e1xyZHd_@|%U+Oly2|46&Lq#xz9UC{Ce@b&$RC?y<8B;VfGHh z$3=&q)9@AAUTv(}hz8esPW1NN^GL{6gTY?O;nhnRcfa?|s}XdPwW- zadG|K_I>$jgUAyf?5Kd2Azuy+Tqyf-jtct|5>CfAlns+UigNhR7gmxQ z?STN0TeKlvr$Jkrwj{ow-?>^EO} z-^};v87nXL9%6%di6ttiC<-`_>YNuo+qC?;dqiqBWNniIT};EjI~T##U7h+X)DoYR zmFy=M>=Y&2X_|uMMtM`q^CzjoJvq6jKtC9%C7%75WGV>q4UHQtO4rMu(qO`tFh;p*-ighggPi2G*+RT zh3XeIett!)-@t;1goq7VrpqmO9973K#Xe(bWY^1raDOb>XYzoj^6ct;W8IbAd?lcj zb#iLMf*0|m7>ma-?UQxzUNdoVx?>nmRgD2UHQFmo&t~JtgE4v(9YPw{yItbQrm^lN zF~`whyNb7^D7OdZ-z$OJLgg8e$rXIew-F!7D zdO?2YEH?M1?gfIx3w{TAL7p#aqgUyC5aVIIncCwNw?2wU)!wK2Wyi(rW<3t)*;7Lb zuU~w78Mcn~+9lnS5FR4_tf~HE_p}xmC~g!?mq>0w5P|A9r9!@x#HYQ9?nH?OR|cdf zh&BFep_KS%?=K?U5nI@4l&7ckHwkWX*X{`L0wZGX4(*!-0}Z1FC!^z9IioZO6N5)U z8`4LY)IVZu8a{Y5K|NmrHx<7?LJmUy!)9|JMY1ZZ*r=Z)Q)6h~X>Lk_y?560TEohG zljKH1jPA{r(|OJWHp=8(v7@p<2yKqN z1pPqm8(hUAgqQoQPYu4A(b~_5oPvM{n@B|+XJzBYS`44p{iH7pn+I{0{0M6!=pj#S zu9x!FhmX@%*kF?&A52k1!Du9eQ~2UpQJUnr&{I`i$B+Eyrn!U5%@wG<1f#@vX|8@4 z_Rfvb%>tmc?pffrg`og=)&uE>xp3w=G9)T@1tf_F=|6R_VIrO}ljrnuN)eczd9-q@ zmX%*C?EM3)aDMTy6YtGdi-C7ff?UQ@XmmD0S1L{_LS|Urje|};GwUYEkm_uJf|!eR z1A6dXzISC64>43iR?Pu$3~kNsk^ceBV`n@-r5Ggyb{}Jx+c+(TZsRrs`9~M|uuf5u4sj@c#5@4lW=$Gde^PI&Ult7Do7}a zfV@NmiMhPDqT=HAK$5BlYI&zfFjx*c8%q!G0W?(##a7xG|YvicWVwNBjy z#f_gOxi$swQ>$v*BtpiEi}@aV79ez054UF|N%NdATXIF%+jic>+}+xIZnd-QSot$m~s+ZL$*aE>x&AsaWi@6+3B%Cm)C{tF6>vXJNb2cg+AJ$GJ^3Vjpa`wS() zC|j=Cv~bfX_|??V2yYSl(#>cX>Wz|y4r4f_JJ5KEIH$)HlcmmSFBvACiD8(k=$KeY&%~Cq&AOc@DUE=~ z;tl_z8e`77TwznMe*6H!{DntOa5e}Hd8+a}TR!*-#~H=~luBw8oo=?76B0ey{p!94 zs4!2yOz_g1hHN$vh#bp5qPfQi9&6Vexi?K6cssryL2B6zj*Q zfesxftMswl&*-;hrB`%cig>mv#;4hCM&=y&4)|dBQu?~PY&8iTpKHNT%b1_fRH_Uv zgi#g5lNLRvQyhwb9%ii&!cZ+mZUjHRbLJdzDJAvRmGVakeE4)U5g3KypoYB6Gv{{n z|CUpH{OzcxS#~T0lkG*ix&_(yG`=v04*0;_P2zKGHk3!i^y#MLPnMw(Q`rc9QRgM@ z{)x=srV|q_giDRX>}w?7E79$5yZK4(2zn`Km4cvD!3GLip&V&=u_4w}sbX}-%#a3t z)OS?|I{whQLGDnKb-}oue((KhDZ%t{%PAUbW@WcH#jWpNXW-Ws!FeJzo`7 zT`D1`uboe>K=w+~Amlh9-NSpJ1g{Y#daX=N7aN1>dIuT4*J8cb0AU%2Nf$e^Px6V5 z`z8h(7c-qIE||XsYaf5bvYE5mv`k}1gCv{4VL+J;s~D>Iuhxw2BjyEl%PeMj2qRIi@YDsgAhd1s zlfLD=6Dv)*|7iY){hSra^)G%qI1l}hFupRy1j(@p0XoX|)nenip=2ulmtxX}J}!)T zi7HI6hK=W$e7zTfuY4IA;4(;_&J{hk5Pm2`K!u%NNS$-UC9+Y$ydA?$Jy>gJFkzY4 zwA~6%{7`uokDp7Ws3BH0m$sq4;Stzm2Tt?FwI{O}X#nS)6vQ7T2H85%5PBSHn}-N^ zY;9lbzFRI?I;eLMU%ISH&lnWiNZDaAj=+6S=Mrud2Zz+3X!4o_^AR>IG_~s8Q2T#q z))5G;LI+{}wC;{vzK=W;q7!6 z0t@&)Y1!EmUmwoB{ajuDqR72N;7&RLGk&He7J2XqL-4s!*@6J)#T*^yAio{zgG({s zC`bP5ALjw|ewaDhoRpntR@>+GLVDeO$I+>NKPPmY3ZG-)J`rk3L6gw;Gbxq8ABFQ6 zDWo8Le!?fa3BUu$Rj60u8~Q!s2u^>rm1?%MU?9O_l*Xq1;}tL>Ihv2j#-ArrG!@2^VD4o@)R81f z@r1Y%G<}%j0#Wmr(42Vtm@V8rxGCKCt`HrOSr9=jxidl1T+Gg@*KuFMaqy1LPm`o8 zPBB67G*Bo~6mEO$ckS&>d{ql5ZkSHh$fX2go#(1XSZQBbHHtuq3x)4nTqx3Gi?W;v zg5Uo%v8sS6T$L%WK}(i#_`v4^Ev=kM%di-JhdQKdEHB&68I_}21ZHiu-Pds2vm7EB zlCbiUl;C6q+xM5PP{Gt#OBJ^d0a}>UwiVnq;m%8vMIRz+ADHAcUM}E z8L2xV(uRu2S|G;rz!}AsH~jha@#<4)Nu6RUTjI;J%bh}e@*P5FF%xT{bydjlnbiS- zD3Te2x$|9qr^L^@abk1$c96-6ml^dctqIi_dXI7&j%vCI3?^tQW#&c(IIQ*hyxz33 z+?b_3ByM`(gQYE*EqVWlJCFrZWhs)72b|j{xt0r3wMc0>uD+0vs#0zppK}_VGa@j0 zyQCIGeKac8&%&NXX+a>gtaQ$N`BZdAS(dG3^vmQ z#-M8UYqyIrV;eG+!uPw*UuW%?Z@vIZ2f1=Q`CXvTSZ8K*^$;qnamKbh*FE!M(bILK zg)?Cfw$1JfUE`+aCPX>u`5bgADB7btlC~o#@bwL;jn8x9rE+sFM99O*z3URc=OtQE z8p0kQXg;7|x0vcaPSwyvyYJ`KJE|MLM2dRJDO@`DLZV3#+t<|PwTm4jqeyj6h+2Gu zVf)@9k37cJVgOZzQtf1cxF6i>K_AO0tnm~+ClPAm^1fhjZufd3_&(iU1hSduZB()Y zca=s>W5s*1mjyiev+C=6Le~=xU^Nq?4&zq^hzai#krv-MwnE|w6=>5nJA1B5#qR8S ze};G-J+rPqY~QOPZT^bEV5ijb<$^YkOQJMZm_wtGqeV@f3Xn^5J5KJ+dGdU<53^yf z(~| zhL$NnGhS%^YoYebHBCq#lX;bG6RyH_)kBI zMmFDfPN8tG(4X8-3o_isdvA0sw2ci?UT_inPX6vi-nrj)jr}&G@|f?XTdE)OG!JHT zp}u_G9>x}v$^WYFBWzcUkotc z<(yIQgnqt9Xm5Vy`(g2hDQR13S0QWN#OSPr8F$`Pgo9cBW*??oSF*5yO-6@OhCV3v67HE3kHeV<8d3kYIFvJ z91Vs!*!2>Sw$=zASqnnF8Ud2VhogeDIFVRO!#VCn;6dB5qJ>-=$Wqd#?gZ)Yb6x z0^k4GZQFUpgP!6^M}2%@I`39ZB~6@q-HoY~>E`TORoQk0r^4M{<(dC2_MN>F-^qBo zF7>5BZ}~2xMs5LiKzEs4eaE?WQ`H?BoSq%)hQo)=wJoinh5o3eq_^`d3weR!A>&R7 z?{qx4NNC?K6Ohrm)L@)R#+1h4JKC ztxB<|hVHn|#dGTs-$4+K&-a7zW_Hz_tKv2HNN>_7vlOd)7Bax0Bc`!poio{tRBJ(# z-JP;x%RU_*Rg|S4n6xoM($T~;DR*1SDJ^h5nWHB_jR~W1S}df_mjk*)G&i=laxAMl zGdBsr53fobF(RDJs+i(qjH!Ns_GkpI&5Yps9ZNDU^2P46aWD9Z49>@O}W zwN*Odjs@X{f5|c>H2)}p6=R&Ljy&ci_Qf~g&#N0hmZHhk)lh2@8*+YFh@%1Zf`RV7 z^z?pn?=I=1mB13cq|1wHI43n^k@rblcH&x>moXZ4Xf}J4zHI;hY40qf;@G-%9U?e1 z7MyMf9w0z)Xq*rT9w7t>F2RF0+5`>m?i)>T2=3YtEFlC5+PJ$n4Y$~Gzx|zWpE1tQ zJH|cyU@(BLTD7XythMHP=kvM=n-vPs#9Yx+Sq_4E?`$r4MDTzyChp2|;n%Qn*L*Ts zn90Kpi>bH9h{Oepnlc(R0P>bf7LDx@m!Q7pOB#Q0LyLWmbT_FKZ7p z`huFuL+{MHUn0erJ4B@AC;`=x-Q$S+M)LS{oZF&Kk8gKiR*TjO+|!3Ng*Q>m%Wlv)Jj&8ZeJ?Q6bx#v4CuGK{IIn7vI6}6GO?l z!X{u&sH*mt1lS8{So)ZoY;B)t6Q2^yizTGdH{s&C0u9>|_i&2e5r;&b{v4!OF6JAK z@_xPG-#DpsE`v9Ag1C5*&M!(-?k z`r+{q|6XtyokHdG)7YQJW@te3rDprvA#T;0p{`W&{(4Z)6mD53Iau#|l#Rl3D*k@R zs!~z$_sN7z)l7Ccy84+x*ktbYH9)oHhQ5f}^JxOvsYV1Z+M;a>bCTWF9=_gDcXj++ z#y1in*Kr4`Os(${zb=-BuS`U;IQn8^!|}vPWGU)wa?jRd&fWNV)^B)MzpZx%9 zzcXXVX-=x%;U0UppdWc^D{5Pw4< zCYYpV&u5zKU2AI{<^g>q`SlenlNi~ae!k^S^(LPPSW~kVPE5?$HWK;KR`l9@h$hbL za}?N`ctY(;cYGdtA8hdDiz1A;h&J$Aj#&W7&^%C#JU)m8jS8OzrM{>?y*uD}!U{vE zmL~RUsldFd17n|+m@G?J1q2C}=-_m$ntqYu5w6wP5*lfH3W@F~u6)(+0r4y()o4gH z_os0OP5Js`ol@`N3zs{6MQ!Q0O0hSRb{ws z2J&DvKiKrli9~EpN5V5F^VU|Se-Zxd?iPx?~ zG2>SIZnB~R|bdsmG0JamdktlJ;9@}SWBk)QeEK?A7TLdL`gnc%IeAB zP7h5hVfg&5G?-KX;YVtTqeoi(bF)W3QgW0eMyb=1tVj5%7?E#(po(HcZrwcxeO;Gz zW)>)+5q9G?idzasS?osVTi1vPsP>xmy_YX8;yfqQn5i7J3gq%(_5sf67C((eOcD`y zFuRsBc8oD9`@xPDMnCb0Q~18S0gGiOSJO&%M?L=Ua_CnG%F!Q z@78qPv!CP@BkCq-Uy$serqRjtD2F=O6pL%Zq;xGPFZ{l~BZ%}pYh@G6jTCrycY9ZV z^{UP({1)ENJV~}7Sr=T;4ySf)`k|&J)Li9DcnlaR&1#L?fu{Gw5Btrw{n&`bE%pVW za^kJ%UIQa|T4VOJ>BBljIM8%m5zkLO26MKb?PYBz-H9vBgI37OmseX|+|RjnbIJwi z&7Q#C74Bd2b_!KLK5qecc+nTUG1K6n^Kj6`~ci_OW9`utW`)opcr z$!Pf8qN?f;VYei!d|Dc-i3NuHdDo!US7+|iZ#B1l8lhz6eNkiGho1g9yE*EQHDDVQ zM2btY+Of${IMLO(v-R#YBTD-nhljq@EZKmR>L2k1OUP5hauxTNpwpRx+ePx~LI-$u zyd~#GA&2udn}`WoH%{b4cr7~K*dbB2NU;xv@`l1Rm2Q>+e)m5Sdg$P*Y7Kyere*PvQoj+1d#Y_*H2f3 z!Z%08qMLXk^Rn(~IQm7*LD${4kTK>17`7$WNv-kR{Y5UNaVuO@!@b~|Xqp~@i^eHG zJ##^XpNEQVv*3q{DNNVPIuwh0dT-Ilj3lt}Q&Qn4>U(B{F55_VD3gHiq1*I%-rUhk zegz6YeLu*BZ1-lBjfkBTC1zk`=IijA3Zg>KCdcmzQhAM;`pZiHN(8oWn*S;h91jSB zAomR%p<1Nb+kty?nV!!opZXAk`8f&A)bzc!k2A1jI0Q83iYjXDM)&9hiwA*4Y}^o? z*t^aX#YH9VQyDI*$RGwkWxrb6Dcb|=i7|7N^K}HImjMg%0ISZWkL3(BSfj4T> zBZ&-zO08hxJv{G|6ZA#@@K1sXf?DFKxdB4gyV0>~G7&)qM|CnrO)ME+Lq&|u+$o=G zZ)9Lju1PF5!FG@hAu#A7Z#Ka@C5gIcNh^2`UPwKwm&DV)^H5Fv+~1tusdIx8X~?wD z*#o&o?cxbw*zoPb7WaLYMild|CaDWvHEXG>{_vz)?2*VWGuqDNKR-UB1*t{GnnYqb ze!L2n%=C(EB5A#@__pPL*W<;UH{ZI?8Tx5Bm~oMBup!6uaM0BOG1FHyU_1WC+-5f< z;CXO5&$(x;KD={a|Qow3PG^v%9gT>sjvy{X7I$sld z#WRu?*!_7+yo6P~uC(V0hnZ%ue?_vxojf~LCkRQE@kKeT! z&z@)n^g%nk?aVI_P;lig^vZ>Cu5-fvY%8DBwMJSMi_*xkRGgPEAa|#aHQ!oY!Rlt& z5Y{yc1+T0*uH@_JB#{Fyicn;>s~SNhIp0X*AXwtS9;iitrDwwfp*GOm=@NE*^PWYF zDk=ML?E67hQkR>2KN)ZB=PPY04>2nxJR1i$5A;<6LPTq^Vg>}H=WAY-%eLtG%9NdH zjeCZ&*QwVq-tFK{WQhjgI)1)gJD1mY%xfs1ars`TD^SdMOgs_gUc+MCWWDyh3Rf|Z zIHgTrw-z=&0ld{l&9Yp^(S?7n#W5TZNUGz3%=uFzfg@xJxuvvKZZF8L-r4iqc2AeeCXKll|h zJZ#440G4ufUOXi_0^G`p;rijjcEcRcg^D4cdLF?_&hYR}GB*b7m|#yUU34xp?Kq$Y zE}SGjQ~tRMm(Xh2SKL@(;^2NDL}A-MWeU*14Vkgq#KE0fp~fT)mbQiGv)UZEPmC@!H`VZA4@iJOBM2>!OI856kQpQizqW0zkC1tc)Mw`x~Kp?$Y)cpvz z+BVX%cDIT-EuuOs5RI+O_XuhUIcfo@ehYfX!=ltsf1ehdcb;YaE+5bxoyNSJs=3EC z&VDr*wi4rSW(-UtX`T18R(5H$o$PnEeIFY04TUBfjQ5`S2 zI=vKK!jj=T&9m#Z*J#H`j%DVYQ@fho8Q$dHMTU1<^JnxeHF})a+O0ZKnM+l3H7U$@ zS;%0^1Kw*%!k{$ufS{qLiZ+8EklY1@te7NE=4RAMUG(41S`S%71{Hhm;x9I;xMw*cka zqU3Gw$ROm#zK>B@w(7JSW%ksJYvg3YSmxod)}NszX~)LL0iSq~@tZS2!2-)TVB zo%vXB?enXmrI`NGFO5JtM{B<$l+^KgFQ6(+54^8un3B$?Fzb!IUZI>hRSfIj#F?1Urg!Igt z9-Iq=gs*Q0YWPwh11R{NKzwD1oNx29c~X+bcbh~eJy;4Rcr5}pha%Lmq;bM|9h;HH zp@YN_*YA+3cws_#mz4o%j z0bRMYo!Ji-nzFFx)1-WD%|uB=w=;!7R(IU?ejRVUgZaACPRBPO+N<5Uh%^nd^zy4B zRbDETu+{?8S5P~?8W|IfJgr-;XY>11U2%uxH}OESg;cB&(b4!Xe!sG3G5DY z18b$FVVwSb2Z=x|+45+a)%X66!tbDGpna}hf!U&$a<~j0MEfqxl19mq%fEr!*E2*w zp)AiifK*T*12oI#q+aZu6#OS0ZZeNjunZ<94yZTQ_283vQa^3Uf|M*89Tz?F(lKu^ zv$c&Wbf*}nj6r_F{biCky!>t5ASbJ!!BQ6gE?dUIz)V^Be1A0&(zC$0Jr(DqHP$?v{&jJXY@Cu0` zl3-bMO7ra7A!0BR3CvoZz0qyo*`d7Zkd6q3NOFf)b+h@&`>@`xr$gm{ME<0>)A?z| zO(o3upBx&X5@yeDNUnSMH);_w%f56Gkex4hL&fJ1aDI3rPC)I3hVQjwttWn;=&Z6$ zk2P%?@J zz2i_hM7DdnX6!zuwLV*2#s6yD0FH*dyByu2?&&3mL7_m>WdbB$lFPr=EMYe#dFKJF8baQom0cxLIXZQL`!yvICi^!SueAenF zykl57NfvO^hC!K znI+iMW{yh~(koSLSCFrkGb{-q%Q-s2L0#q2mX)LxxGjqXBE#0da;LT!aYj+Kc{{dT z_YA0ZdIMW{(w^$vGHhHLe(R}r&0M5Ew0P#ajxHUJ_f3ac+a zQ~AKtr=XK=V}0IMtxIU@7{EQeP1i_z%glwusE4VF=X?-Bs|Z~UGr77>Rl4<)pU;KY zVlN1y4&~Zn=Yh;uo>t`II8dqksD8ncPfW78qZ6oG$lb9pPyV5Af1dX^3x0sc<{(zP z7~v@64UCwwtygSt*>;I&(!aoHQa4hZ~yuz|e(4fJDV>*K6zH;fiNs z96**-IYad91XZB=^?p8QdLVD4$@K2Y8<9(xu|kg(kgTpw-Mhc5!P5x}q>me-p3Th( z6p@}Rw<)8Y+jc#q?t2#a!6Av+MlZ|S zsXT({52sh(B-V++w%^}B^t;{hNtgd!aPVAuyt-0p)XGXmxy{o;a(`=SMJ6a2hqgk? z2$X`7&(;DxkSVGunEYxTXa57;4@PJkTjX$RI@cxz&RlYpqPg6)#So3jd+h>Db{Mr` z<@R7o&OJUu!#0||S#8IGS1DOPGv}6X^!PK}T^pAt?Hj)3Z`sCfS;i{nsQM#2luhF3 zjk7qQce`0@2N<5WAL5EYrxzj$*iY%$&0j_aBs=cn@I-aI)0A({wU(ZG1g53oEA*(x zRcTPD0%~z+6882+`pY)#5;5d?*oq0{>v90QZK;X@i?RY3S>5Vd&HM1z~V0@-W3jW3-W(A3*pWW0F2!MsDOHhKApHW1s zCg!F$a{LkAY9_oL5lb)luI^KQQ5#EpbG0@8M}p18lPZXhRYf0po1lY>_-3xQ?SlX|o86yE-3Z}xsw zyc97<34kWjs?73c3m8SG^yPkrZN})l{QwGglt9Z-<(azD9!$Zqb`B4@ufdq-jpl3K zc4-A>B{E}Wa|Ws!zscFB&$xZ~B$ zHUA}1%$HwAF-no}UukynEWFK24hR~VC&2}o)ErMvI2PV=pB1A$N(L^p35FH(BB-g0 zXtVil-}zwqYLl{)Wa<+H<0u}_6;?%9 zPjGAz3XlM|KDo&m5Ocr4_oY;$S8)YY?c&vVc57mRQ;AnOzn8@;zEnS(nM@E(@ z0OznK4_w_RYBT-05tqTLCi$s2^r4mcb@gW4F=vo7ooP6a?AK3jp<@S&)Ais23D8kO zQQ6p)V?-veW4_{}?d@D{`AAv@0_rr=6O>Fd?);!ZKfy)LXPv|$Fl(B5M4+6-_YIfE z&^OePSk|L(Ag~^;3??4aI9*yb;%&bCj%jW&^+oSgRMd6z5d?l2JdU~e-7(GmXw4gS zMB$qLLhZ07uV+Tzj&he*hwymxYc?vOzqd409 zH4S4qL;GQQbk6d&>+F+P`6dN>f5co}!bOBQgL#I1Lp>(U1D{GlCZIcU(1VGT>qUN; z8-Xz>OoEL%?KI-S4Ed7JU#BDF*zCteF^+cM-vD%7FcO|)+w+FF9 z>-M+WFOoIm!ws=kGxGSh?O;arh6&oU_*+&=5BwC(ywj#cU+7%r+ZQoNOpIunXVls_ zeN2uIOCFH?Jlrv1;~ZBQ!_C%HBID)q-mCz=$3}^_+z<_vHIUx z3h~LNL9#}LQ3sOf0J4z@LfOl<>sKTLr9Bl%=OU^Es%#46w&I>Q>P0OiM&Le3?Dg@9 zZlhb7XSOfoE|f{BdRa#00$s3zARhyOFyn(9~}pF z;bE+>Nl!AKy@e_l&zN=y>dJ1wf%K|>*J}}=9z=vcWXZ_xF-fiJIu7e_87~hwTge%1 z#N}(W`9SHU5mWA4gFl|eJKvz#sVCjrj&DN-<4_(adU0~NK1*7TUSfP!h^?)s`jbeY zbscS0*YgRk^Moqy3yDE$dd$_I(Hw{sy_2h(b`J}fzRJcaz{+txX><#yls-Q9CY0Rb znXI$V@!Ufq4x-%sbDXT_RJE(?2AgW&y4;GznTR-EY6pd(nP039)iX8IEp-JN>+SuuX%&M^MTV#PkRg(z+Wn$6`0vh4`F> z(P!eFv>%$8om$Qo+qi};nIhX+JbS85ZZYq-jwLpF?guLv< z2*hEba?G6}j!8@LEiE*?(_VZs0*)zQN-3NZnbCsnC-_x(r9?`2Q}THpGY zWqNPJ%|exZtzE}NPpZJ6lO)ZOsa}xfyP9R7!ZWV)t*AxI#|5*Wa}Pzo)E_?L#=_KW zDcM(z_7N+&?U%W^?}Kh%YRB@hQi%R>MD5aR{;&f&virU6^RMZMC}m4^m51}kAw&2z z_w?AB1s-|Ce~mZYk70gn*dZtIO{F?@aKs%t5f3b{SrHKxsr!YJerW9f9xU;jLlnm-r_|3tihD9D2rR3cj+wj4Tr)zn3E$E@GR6x3KHWjS^(>h%zexau|O{nyZP zRGUJ$@DJOCCzt9qk|7my%dC*XJ~L2lkVz`y&cd%bewP4OaMOE7PJec2mhCB)#B3)U zsFOF$j7?!F6mp8t?9+hZS=Zl43T50Z7aMsEch(1?l4}! z0&JYjljZ(cUzWkgOI0FWQEEv0V{Pn*z!uL|=%rF`r%5Pkx=VCX zq<}$m+m5PnE<@a7X`pKOG(+WuUM3lZ5&3V|=M;KIuBRP5ZX6@~9X;`CUpOufw^2ns z-E9cY14k5hjLMYp>DMgd8M%{*orvfrpiHfZa5yE)cleozmnu;2Ar`kZA{;aq`W3F{ zADnv9Pk!izdLmnrSFj{k$UH?zqc_xY3Wxg`Tm;Mp?L$?HnA{`s1zgnafDTwi)Vp)n z3@bn({hwLMS-=wunY|y50ouk0BQ$F`MV$EBt0#jr>|q>x%mc)onohB;i|-YCA6?+r zkJ+e1x-w!txQ|NjBkHn;ZeC^FCFc6yPE}D`fqNh|C_MRk6(~IHxPb6$oER$V1t+_W zy%`o7J?@nSQDab);kg6g4pq=!+nG8YcfwM3A{+PS#_eO2^j&AFTgqtzawk>x`8IW` ztruL{*?2egCQ$Mk2tR=M~X$zK#k>;8gRvcG{T z^ci=DEZ?@f)fzn^jy2S5reYmVOv1Pf2+#WT79<$Sem|u2Zn6$H)dR(G| zmPWjh7|ywxJe~J(B10*HFhm5wm&o6qi_O|9>A<8Q-5Vfs@aQ4~-6C(rUR2ERuWGn? zj6mtSYCG z;*1jb#K3GUWhkSy7jKo+IO@{%{NVmUo2=KOOe{mbqYweQ+^|!A{cw+QBib>aB}Yy0 zJrjiP61VE&={n#1tNY0%K0e}BKR>f`*)~6URDbqc-JR&u`3ej_{-c>7d(<3UUkCVQ z=n{~Je2lu@+kTxv+)W)pAEo;KAaf#Q#uGc(L4U$)`!U~kd6l`J1C-vyra-3hnZcoQE|)fg}R&L*QtjOR1Rg74I%!WKDEz6r)+u8qkJQjZ0Hug7_&T-(Vg(6 z>Y@0_$u%QjSdwmPUHb-!!Nu z+KuPhNiewX5%%)Av79_E6apT36SZfJ^Drr1@_AV&b>)ng^$r%i7jx^*6?*Os2dP=f zsXzXB6l{N93p*j}&GnAR-Y@pu2gfQ?;YliLdFJOg3HIlCm`P<$tg_u$1 zVDEqMjUo{y2TSubP56E@z!oqewU|2o#D!&9my=D6$zEunOq-9%|E1`B(sVfq_fx3G z+UtRX%xs+gw~L%m(fKFyLocgWQL3^`3|)!(P`TeWh=pFqXt($u3X+T8jS^g?vo7N8 zul1f_a)nZ)_7Rf5_(ivF^AzpnJfLOAVyz{9aMw;uPwFnMq+_#Z{rjTvYoe7Q@vB>l zRIixXrR&9e1@MR5&L}aITIomYTrB89;}<&4Cl-Hbn;l75IfC!u^jM_#x1tx-@)a8i z(+4(?vY5CI3HRbv-!{+?&jqp^?H+z!Sxpr*&QGQ{s71bU>Q@n>RttLY#8(of`K_%5 zCh<#ApoTCtXo2k5(|f;u-^DpF0XtjZC87vL)g^uhdCb#dz;>JnY!mhIh+|@EdR%u^ z-2&fs&47R8b#FlA9#7Yu_uW4TcE7LtneQs|JJo`%bfYykwNTeOnYA%O0X<@2ct)^SFc zm(^sHi3acYs&BY`I|7p~2IpO0;LF+URwOkT%0y;E>1{raLgTrod(%g7bNfvfeO91U zB4F3@e%~+4!Uj8Xn@GN|vP2&!dISns)JUpq6gSV!1A=iC=3>j6+VOmLgmVU2yP1K^=%|-kB-FR6*&+vUvRg3L3m$EiJZ5O_0nfj~c^CVE) zH?_!j(agsIOcyVTWs~0crWlcU?lFu~w0CER z3kyWW!mZ*t=>a*q&3El_x{=K{7#pq*e4JnT=`i?LkKB{8YoI1SR&2+TrVa~APxPy> zn%bg-LFf(an{3P(!`1m9wf@u&saA4n7yf+ED=KcC8f)uB{xinW5j35s`_J}RBy;#o z<{B_>+u$Hw-s=Q~7D1{$664~z(UQcYoH6(Jh;y0-s`liG9+JcEVV%+$P!fG&I#9ibW?`1*l_8SWh ze1cGCm!)h=-NC|rNsd*5O|2suxsRsTTaSYc46_Pf)obadp$F3N+$484BSu7h;!Es|=QQ&lJh zgC650xGU|53@R)5P6B($TCr@+c(YsN$6@o2oy>Ss`3&beJAgzbhimm{q>$`pAoBlf zAUf~@vb`zB+$bF$E0@5b3~BakOkF0%<3x^9>j&73A4Uo_WK|;0li(B|kY>P(IO%SE zX)T6}zhPbbBtdQ`dOO$i+`%4Ud%#?8>yCCFY1T=s^YWdgH+v{YN*PycJ^3K$okA#S zgygLMTi^B`fd&K)NYxZifd9t}YQIZH*3y=lF#jR0(U0(Y7Ue;O{UB|3<=7Ii$5zE- zqa`-(orV$+QhBF-)%J&NVS8qhNo62z~vZjw|=~V#dm@>!TX7I?bOA)Ah=W0O51*mT+zS zP&z6WpGt80OLu9NQrKKkX@krj)!wjt6>1;@?bmd2L34l%f=s^B8?mu_BG^=pdFtOG zE11T|tqG*l05v0brBUhu#RqyM1N<$9juL>&&BVEj13Eu{w!x9AMuEeVT@rbzZSE9n87) z1j>g#q>|xcu79!UANhIjo0e=)mekVuVVR_}%dSLv-b?9c@xE4hHbIl!Zg%5{gd%56 zo-W9Me~A_`pI`RaYU_TL^Cr0|y0-G_^&80NV$h2*)KsV%4DAvGIJ5R`{Dh}&N?z`D z=4tWL>L|>n9VD>$sFDNR3fg?bXV!(xt}Y0P$!e?G%PZ8b=%S(I;FNt`+@tM=-X2G_ zQAu{hIkD&vGCS&Ok%0wWy}_Ms?_HjJpUyE_ZEjx}RC;HdO|!U;@n!DySy))+Jv{QH zM{I?sB}XTz%kM*2q|6yU_{srnVCfANJv^KxNuY}l^fN2E|Df@^CEk#nF9QEX{@B>R z#aBpBZ8GRz!+0iPMo6&mAxf-v&hSv}V_G5a0jkKb^&_eF-Ag@BdtZDSi4jf1rd#s_ zJ~1<%K)E*&n8VM_x3ax-X;XlbA571%IRNbJWP03(hsS_ZJqVjXpyYyel0EG}co zIZEn^3E=&~PiS;he&^Ij3H|PUq5l_suVF6=U<;=l)P-dR(+%fL4%YSpO7E5MDMx;O zvIA9vm5a-B$^L=l%)UrRV?Ab04$^6dzb^l6pW*N@mM{y;k1@ zX2oR3ur<3z-RJJmD7a=%D}W0gS!B@G&$1}gp4a2%v`>#Ol%jUS$$ zB<_?rCb6S$H}}*Hg@2#?msob#2z8snORsd6;vx2ytEG{#MTUZ*$U`}M&!+u+ z$ut@+n&cT<)~h<(a7pZbhG@;uI~qOPpaj1RN}z_RfEBkO?Psh}%rmAB^$XZJe_j zqF3pfDMX%cnKQ?vFQR8oXA@^ZNBeb>yk=hj?uxhrC>Z6bvNMQE_;_6AaL0+1*O|Fw@QT_DEr(5>2ZgIOJq{L-b9r20rtqr1Wq zPSCwWIbMC(a>i5q9{PqiA02E2yH1vOj?*@bsi~Cul#+XgL$luLemSk@ytw)j^qY{?i zTh7ED7;;zk{b!%wOTX!J_dnfNZkTo{y41`Yz)=XGfS=8h@$IpB@E^yZ!v&qggsay7 z>p|2FejkBWf9?7=?D^1ww12a03!QFTyca7a!D2wLqjAISKZ5ll{E}5zG5#H|{o%Iq z@1_t+e@1aNY*0#NmRUG;J@Mys_S?VRkI6s}UJ))!EHWoXoO$&-08sM(Az~ld2=V40 zFy;#)Wmc(~>DX>ha6AYmzVQ{ney-VZ?KzSA6=+#E6pX7V4ixD6g(>}e(}0$KfZoJH z;dJSLfs5;JJKBfe;Hi79lX}wzj@!-592h9PBV_EYqvU8;0qNmvtsEy-pmxagYpnlt z1zN)Uot@6|XK`IUfKvp6LgutDl+_9?>@rJz0*1+LU13om1Qzfx*u%>i0-I<-#Le83 zw4mdS4C~!*x-oyaLHzx@R0hKhL$~xV5G$(V4&X@ee|hehu4=H})$!QL6&*tS>bN() z(1`yG24XjNGU(A?!GHp61O^H4D{|1%Y;_qvsuFsMe;&gvX_wqOH$fz;0U|aR85Iiv z0}trIKk?=Nu=@CmFK>qp3dl7ZB#jeS$xapuc>Zku47ti$AR!3|H=BNcI6nT5dW#F{ zDQR9BxIV{ju2;jXoA)2fR(xVZBp}@g@IeKzVad z3rTLg7>57Kec2$9tNQT7lBt?L;#Z#)*6)Buar#9a^$FhWD{YhWWzV(Fwye4weCLfa znttyI>#IFK$8q-LOloZto){e^ve%$15bMnGu(r;ZYa$0@C*-I;Jj z085%@`7>wjn5dq(7mww`$G#+NuDY)U?ADMmpgPQJsj`>e^TufQ)@;Z(Rah5@ZoRuC2Wv_Icq~ z!)CfEOCwi@lc)FI>+Vce-O%gVt;S3Fev|EsVa{8@Idb_{YWAOw6uo}G2Q~ITgW7@b z-VF}_%@>nooO-f)!uflEv(%EOs3@+U`*`-#o+- ze>qT4_+#PG875x6ZIvbxqs1vuA#&zRoZ88L5qv(|K@S5Alu zpBUbfAblG^iT@$Xo|hiuUIp&2NgN5^^!R}0=8!@)LqpS5Mr@aam}hqb=-GIm(aRZ0 zz2-BcIKT|zoIPxAqq?#i;z#YK*S~($*mf72h+fL)7RK$r{qTi73)Wj-77|2@aCkwe z22IoPqV$?e?C0kBw}1QdgElx$Zz=T0!D=#G`W~a2b&RSxFkz!T%}1x_+p!<7?C@Xj zBE09SNt6ixevZG+8iG6Eox3dmwWUP#-y+)1zI48G6|a$K`Plx+kn?TJlxiJ2lLk~7qd7q7YQmFLOkYn0 z+`*T))8#$D2GOpoc>LGN|90(cEIg0s%m}@amsiO1rQxx|RlA<`yv=ShhsB3r2XQ48 zG3YUm?XA@yp@pvLfVKNXc`i~~|8POqMQwHO*8rdSxyDQ*^Tp#_r}w`YR&9Y}&?>2( zA6kjkjOnX_dt+|Nkp4%%16LE)R_COpTYn#+_J}{$LR*>)^drV@tzmKe(Y8%%==a&$ zKCA+o5Ek5jx)EG>w4Ms?JRX8(6s54{=L=q%P$nblC;#Q<{&nr2@z}th?R+l(cHjT{ z(@nR~Ye?-!eY7C^rxO7mDSVU#D{R3y_@|2iZqf%ijBeQn`5OPt!Lu5C7P2oed)5A% zwvpT;0|h6^6aPaG-5h-5c1X|$4ZY-l(>4V%_5Z!_f9U!Dx9|8BhFt90!0pEYoVS24 N#V4wAr7{M-{|Aw5)^h*= literal 0 HcmV?d00001 diff --git a/images/passed.png b/images/passed.png new file mode 100644 index 0000000000000000000000000000000000000000..02378b089cf4d5db792e16bc6c7a264923479e1c GIT binary patch literal 49230 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 literal 0 HcmV?d00001 diff --git a/images/release_pr.png b/images/release_pr.png new file mode 100644 index 0000000000000000000000000000000000000000..2556c05de6cdca58fd218d570bb3051bdfe56287 GIT binary patch literal 17794 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

    `1nO;z)X8rMVN@?YF3g1 zYHsLB^S0P}Xt_fpJy`CKytLpTgR}nlw#1&$k^{UI8^Y;QlJwYp&t@NupM2c*mH%jaD44+dh%u!F&HjFWd4cJL_16+Z`CK6}ha~y407N zdD;@NB{qyNPleQo{!Ahvc|B4#l&f=G9Y_`;l=jKqm`$)Yynx_M+Qb|1-S^i(q5IY~ zp}ba)e<9CzHGVU6Z@2r-koAyt?SRNOB4>uo#cRBxjtK%%eY6H5+ABPMg@w!4$qF5* z)C&?K9(Mhk4L>hz-?qo)^LBOLx+c(RT2z8>Rx(58vY$*Qh!_8jm+76MEWLF|w*;PjtizY7qZVYhaSkZN_gNw{Nm|EMY>M8N*vaq?rBx&rJUq zNN=*pKXLAFsh?TS4Ta20OQv~=9uo^+bJ;ZYdtBMa3Q zap7a$;h#@_INa0zU^YK+jBH{vJD-=)nfo^W&wH+Oi5iHWKX=0R)93ZKRbqFQ1nnr8 zSW$XP#0;1O3yKpyKRa9W>x-slh(i1x^*y_o^u!xv#dy~BYIBMv%>&we0D>vk1O|1NI zu>DG(yX1C8PeVPkOKtpVG70Q5S1o|Ud~LuLO`hN0-e2D`*<@wIygfhOyZL`uYw=t1Fp1D?To2 zQRAJuXXoSgc?Gr{D;kPEK2nX`oVWOcex4M_wH!R`mzMbHp0^KonxyV8HiN5q&$R68 z>rU=tZhvy=>1oG%!Iy0hGahEl=TqV4o%+Tfju;cE4xRx$yMuG{w&f0wf?d9h-S z_5bNcb3#A<%MmD1oXl4!c;>^i=7;@98T0qO_^^M9J%5Xh!pfkfQ*>q;d(Dgac)v5l z?5_7a&K1??=2)sv2d)~ry*Q^<`e~{uGrllC4hwm0v>5(c5~=tzU(6iax4@!wmfYR3=o>Oe_0t^jPY=z zMZp0MjlzUknn@N3a5!Oui~*C^p-4yB;7DWUvW|xhaOiNT2xuAS>Gi{LegR@}A>cU;Q`?+&#wN>FVdQ&MBb@0G9~# A!T Date: Sun, 26 Apr 2020 21:14:36 -0400 Subject: [PATCH 02/18] sycning fixes for april 26 --- .gitignore | 1 + provisioner/provision_lab.yml | 6 +-- .../templates/index.html.j2 | 18 +++---- provisioner/roles/control_node/tasks/main.yml | 1 - .../templates/inventory/windows.j2 | 33 ++++++++++++ .../tasks/inventory/dynamic.yml | 22 ++++++++ .../templates/instructor_inv.j2 | 0 .../templates/windows_instances.txt.j2 | 53 ------------------- .../roles/populate_tower/tasks/rhel_90.yml | 2 +- 9 files changed, 67 insertions(+), 69 deletions(-) create mode 100644 provisioner/roles/control_node/templates/inventory/windows.j2 create mode 100644 provisioner/roles/manage_ec2_instances/tasks/inventory/dynamic.yml create mode 100644 provisioner/roles/manage_ec2_instances/templates/instructor_inv.j2 delete mode 100755 provisioner/roles/manage_ec2_instances/templates/windows_instances.txt.j2 diff --git a/.gitignore b/.gitignore index 65c7d43f2..d4b4275f2 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ Gemfile _site/* .tox/ provisioner/tests/ci-common.yml +provisioner/aws_ec2.yml diff --git a/provisioner/provision_lab.yml b/provisioner/provision_lab.yml index 3478618f7..09f3c6d15 100644 --- a/provisioner/provision_lab.yml +++ b/provisioner/provision_lab.yml @@ -164,7 +164,7 @@ - manage_ec2_instances - name: wait for all nodes to have SSH reachability - hosts: "managed_nodes:control_nodes" + hosts: "control_nodes:lab_hosts" become: true gather_facts: false roles: @@ -177,8 +177,8 @@ roles: - connectivity_test -- name: Configure common options on managed nodes and control nodes - hosts: "managed_nodes:control_nodes" +- name: Configure common options on lab_hosts and control nodes + hosts: "control_nodes:lab_hosts" gather_facts: false become: true roles: diff --git a/provisioner/roles/aws_workshop_login_page/templates/index.html.j2 b/provisioner/roles/aws_workshop_login_page/templates/index.html.j2 index ab69b108d..710925fa6 100644 --- a/provisioner/roles/aws_workshop_login_page/templates/index.html.j2 +++ b/provisioner/roles/aws_workshop_login_page/templates/index.html.j2 @@ -219,7 +219,7 @@ $("document").ready(function(){ $click.click(function () { $clicked = $(this); - + $expand = $(this).parent('.header').next(); // Expand and collapse content selector //open up the content needed - toggle the slide- if visible, slide up, if not slidedown. @@ -231,13 +231,13 @@ $("document").ready(function(){ $('html, body').animate({ scrollTop: 1000 }, 500); - + }); }); $("a[href='" + window.location.hash + "']").parent(".studentinfo").click(); - - + + }); @@ -398,8 +398,6 @@ $("document").ready(function(){
    -{% for host in ansible_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} {% if code_server is defined and code_server %}
    VS Code access
    To login to Visual Studio Code via your web browser please go here:
    @@ -451,12 +449,12 @@ To login to the Ansible Control Node use the following for SSH access:
    IP Address: - {{ host.public_ip_address }} + {{ hostvars[ec2_name_prefix+ '-student' + number|string + '-ansible'].ansible_host }}
    -
    ssh student{{number}}@{{ host.public_ip_address }}
    +
    ssh student{{number}}@sean_placeholder
    {% if dns_type != 'none' %} DNS: student{{number}}.{{ec2_name_prefix}}.{{workshop_dns_zone}}
    ssh student{{number}}@student{{number}}.{{ec2_name_prefix}}.{{workshop_dns_zone}}
    @@ -479,7 +477,7 @@ To login to the Ansible Tower UI use the following credentials:
    {% if dns_type == 'none' %} UI link: -https://{{ host.public_ip_address }} +sean_placeholder {% endif %} @@ -495,8 +493,6 @@ To login to the Ansible Tower UI use the following credentials:
    {% if workshop_type in ['devops'] %} Lab Guide: http://student{{number}}.{{ec2_name_prefix}}.{{workshop_dns_zone}}:8888
    {% endif %} -{% endif %} -{% endfor %} {% endfor %} diff --git a/provisioner/roles/control_node/tasks/main.yml b/provisioner/roles/control_node/tasks/main.yml index 874691e76..32b19d3af 100644 --- a/provisioner/roles/control_node/tasks/main.yml +++ b/provisioner/roles/control_node/tasks/main.yml @@ -117,7 +117,6 @@ dest: /home/{{ username }}/lab_inventory/hosts owner: "{{ username }}" group: "{{ username }}" - when: username in inventory_hostname - name: setup control node for workshop type include_tasks: "{{item}}" diff --git a/provisioner/roles/control_node/templates/inventory/windows.j2 b/provisioner/roles/control_node/templates/inventory/windows.j2 new file mode 100644 index 000000000..d32adc5b8 --- /dev/null +++ b/provisioner/roles/control_node/templates/inventory/windows.j2 @@ -0,0 +1,33 @@ +### Inventory for {{ username }} ### +{% set control_node = ec2_name_prefix + '-' + username + '-ansible' %} +{% set win1 = ec2_name_prefix + '-' + username + '-instance1' %} + +[windows:vars] +ansible_connection=winrm +ansible_winrm_transport=credssp +ansible_winrm_server_cert_validation=ignore +ansible_port=5986 + +[control_nodes:vars] +ansible_port=22 +ansible_ssh_user=ec2-user +ansible_ssh_private_key_file="{{ playbook_dir }}/{{ ec2_name_prefix }}/{{ ec2_name_prefix }}-private.pem" + +[{{ username }}] +{{ username }}-ansible ansible_host={{ hostvars[control_node].ansible_host }} +{{ username }}-win1 ansible_host={{ hostvars[win1].ansible_host }} ansible_user=Administrator ansible_password="rSzVIZU*b$z@DwDsrrzubPezBm)j2-Cg" +{% if doubleup %} +{% set win2 = ec2_name_prefix + '-' + username + '-instance2' %} +{{ username }}-win2 ansible_host={{ hostvars[win2].ansible_host }} ansible_user=Administrator ansible_password="rSzVIZU*b$z@DwDsrrzubPezBm)j2-Cg" +{% endif %} + + +[all] +{{username}}-ansible +{{username}}-win1 + +[windows] +{{username}}-win1 + +[control_nodes] +{{ username }}-ansible diff --git a/provisioner/roles/manage_ec2_instances/tasks/inventory/dynamic.yml b/provisioner/roles/manage_ec2_instances/tasks/inventory/dynamic.yml new file mode 100644 index 000000000..e9ee11f0f --- /dev/null +++ b/provisioner/roles/manage_ec2_instances/tasks/inventory/dynamic.yml @@ -0,0 +1,22 @@ +--- +- name: customize dynamic inventory script + template: + src: "aws_ec2.j2" + dest: "{{playbook_dir}}/aws_ec2.yml" + +- name: debug it + debug: + msg: "{{ playbook_dir }}" + +- name: refresh AWS inventory information + meta: refresh_inventory + +- name: debug it + debug: + msg: "{{ groups }}" + + +# - name: Generate instructor inventory +# template: +# src: instructor_inventory.j2 +# dest: "{{ playbook_dir }}/{{ec2_name_prefix}}/instructor_inventory.txt" diff --git a/provisioner/roles/manage_ec2_instances/templates/instructor_inv.j2 b/provisioner/roles/manage_ec2_instances/templates/instructor_inv.j2 new file mode 100644 index 000000000..e69de29bb diff --git a/provisioner/roles/manage_ec2_instances/templates/windows_instances.txt.j2 b/provisioner/roles/manage_ec2_instances/templates/windows_instances.txt.j2 deleted file mode 100755 index f75e306d0..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/windows_instances.txt.j2 +++ /dev/null @@ -1,53 +0,0 @@ -### Inventory for student{{ item }} ### - -[windows:vars] -ansible_connection=winrm -ansible_winrm_transport=credssp -ansible_winrm_server_cert_validation=ignore -ansible_port=5986 - -[control_nodes:vars] -ansible_port=22 -ansible_ssh_user=ec2-user -ansible_ssh_private_key_file="{{ playbook_dir }}/{{ ec2_name_prefix }}/{{ ec2_name_prefix }}-private.pem" - -[student{{ item }}] -{% for host in hostvars %} -{% if "student" + item + "-" in host -%} -{% if "windc" in host -%} -{% elif "gitlab" in host -%} -{% elif "docs" in host -%} -{% elif "rhel" in host -%} -{% elif "ansible" in host %} -{{ host|replace(ec2_name_prefix + "-", "") }} ansible_host={{ hostvars[host].ansible_host }} -{% else %} -{{ host|replace(ec2_name_prefix + "-", "") }} ansible_host={{ hostvars[host].ansible_host }} ansible_user={{ hostvars[host].ansible_user }} ansible_password="{{ hostvars[host].ansible_password }}" -{% endif %} -{% endif %} -{% endfor %} - -{% for group in groups %} -{% if "gitlab" in group -%} -{% elif "windows_domain_controllers" in group -%} -{% elif "windows_instance1" in group -%} -{% elif "windows_workstation" in group -%} -{% elif "managed_nodes" in group -%} -{% elif "rhel" in group -%} -{% elif "docs" in group -%} -{% elif "ungrouped" in group -%} -{% else %} -[{{group}}] -{% endif %} -{% for entry in groups[group] %} -{% for host in hostvars %} -{% if entry == host and "student" + item + "-" in host %} -{% if "windc" in host -%} -{% elif "gitlab" in host -%} -{% elif "docs" in host -%} -{% else %} -{{ host|replace(ec2_name_prefix + "-", "") }} -{% endif %} -{% endif %} -{% endfor %} -{% endfor %} -{% endfor %} diff --git a/provisioner/roles/populate_tower/tasks/rhel_90.yml b/provisioner/roles/populate_tower/tasks/rhel_90.yml index 90fe575e0..1a8fad204 100644 --- a/provisioner/roles/populate_tower/tasks/rhel_90.yml +++ b/provisioner/roles/populate_tower/tasks/rhel_90.yml @@ -17,4 +17,4 @@ my_tower_host: "{{ ansible_host }}" demo: hardening include_role: - name: "ipvsean.product_demos.install_demo" + name: "ansible.product_demos.install_demo" From a061e69280799b20f31689bce1393bf6fc80af8c Mon Sep 17 00:00:00 2001 From: ipvsean Date: Mon, 27 Apr 2020 18:30:00 -0400 Subject: [PATCH 03/18] windows is working now windows, network, rhel_90 and rhel are all working with dynamic inventory now --- provisioner/group_vars/qradar.yml | 3 + provisioner/group_vars/security_windows.yml | 7 + provisioner/group_vars/splunk.yml | 3 + provisioner/group_vars/windows.yml | 12 + provisioner/provision_lab.yml | 6 +- .../templates/index.html.j2 | 4 +- .../templates/inventory/devops.j2 | 22 ++ .../control_node/templates/inventory/f5.j2 | 21 ++ .../control_node/templates/inventory/rhel.j2 | 17 ++ .../templates/inventory/windows.j2 | 6 +- .../tasks/create_inventory.yml | 27 --- .../tasks/instances/instances_networking.yml | 4 + .../tasks/instances/instances_windows.yml | 4 +- .../tasks/inventory/addhost_devops.yml | 83 ------- .../tasks/inventory/addhost_f5.yml | 85 ------- .../tasks/inventory/addhost_networking.yml | 114 --------- .../tasks/inventory/addhost_rhel.yml | 57 ----- .../tasks/inventory/addhost_rhel_90.yml | 4 - .../tasks/inventory/addhost_security.yml | 218 ------------------ .../tasks/inventory/addhost_windows.yml | 139 ----------- .../tasks/inventory/dynamic.yml | 19 +- .../manage_ec2_instances/tasks/provision.yml | 4 + .../security_ec2_create_instances.yml | 2 +- .../manage_ec2_instances/templates/aws_ec2.j2 | 42 ++++ .../templates/instructor_inv.j2 | 20 ++ .../templates/instructor_inventory.j2 | 28 --- .../templates/instructor_inventory_devops.j2 | 33 --- .../templates/instructor_inventory_f5.j2 | 28 --- .../instructor_inventory_networking.j2 | 33 --- .../templates/skylight_docs_userdata.j2 | 6 - .../templates/skylight_windows_userdata.j2 | 24 -- .../templates/windows_userdata.txt.j2 | 11 +- provisioner/security.yml | 2 +- provisioner/windows.yml | 2 + 34 files changed, 193 insertions(+), 897 deletions(-) create mode 100644 provisioner/group_vars/qradar.yml create mode 100644 provisioner/group_vars/security_windows.yml create mode 100644 provisioner/group_vars/splunk.yml create mode 100644 provisioner/group_vars/windows.yml create mode 100644 provisioner/roles/control_node/templates/inventory/devops.j2 create mode 100644 provisioner/roles/control_node/templates/inventory/f5.j2 create mode 100644 provisioner/roles/control_node/templates/inventory/rhel.j2 delete mode 100644 provisioner/roles/manage_ec2_instances/tasks/create_inventory.yml delete mode 100644 provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_devops.yml delete mode 100644 provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_f5.yml delete mode 100644 provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_networking.yml delete mode 100644 provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_rhel.yml delete mode 100644 provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_rhel_90.yml delete mode 100644 provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_security.yml delete mode 100644 provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_windows.yml create mode 100644 provisioner/roles/manage_ec2_instances/templates/aws_ec2.j2 delete mode 100644 provisioner/roles/manage_ec2_instances/templates/instructor_inventory.j2 delete mode 100644 provisioner/roles/manage_ec2_instances/templates/instructor_inventory_devops.j2 delete mode 100644 provisioner/roles/manage_ec2_instances/templates/instructor_inventory_f5.j2 delete mode 100644 provisioner/roles/manage_ec2_instances/templates/instructor_inventory_networking.j2 delete mode 100755 provisioner/roles/manage_ec2_instances/templates/skylight_docs_userdata.j2 delete mode 100755 provisioner/roles/manage_ec2_instances/templates/skylight_windows_userdata.j2 diff --git a/provisioner/group_vars/qradar.yml b/provisioner/group_vars/qradar.yml new file mode 100644 index 000000000..37decbfc0 --- /dev/null +++ b/provisioner/group_vars/qradar.yml @@ -0,0 +1,3 @@ +--- +# group_vars for security automation workshop - contact Roland Wolters +ansible_python_interpreter: "python_interpreter" diff --git a/provisioner/group_vars/security_windows.yml b/provisioner/group_vars/security_windows.yml new file mode 100644 index 000000000..830d31033 --- /dev/null +++ b/provisioner/group_vars/security_windows.yml @@ -0,0 +1,7 @@ +--- +# group_vars for security automation workshop - contact Roland Wolters +ansible_user: Administrator +ansible_password: "{{ windows_password }}" +ansible_port: 5986 +ansible_connection: winrm +ansible_winrm_server_cert_validation: ignore diff --git a/provisioner/group_vars/splunk.yml b/provisioner/group_vars/splunk.yml new file mode 100644 index 000000000..a8b6bdf67 --- /dev/null +++ b/provisioner/group_vars/splunk.yml @@ -0,0 +1,3 @@ +--- +# group_vars for security automation workshop - contact Roland Wolters +ansible_python_interpreter: "{{ec2_info['splunk_enterprise']['python_interpreter']}}" diff --git a/provisioner/group_vars/windows.yml b/provisioner/group_vars/windows.yml new file mode 100644 index 000000000..b42bb40a3 --- /dev/null +++ b/provisioner/group_vars/windows.yml @@ -0,0 +1,12 @@ +--- +# group_vars for security automation workshop - contact Colin McNaughton +ansible_user: Administrator +ansible_port: 5986 +ansible_winrm_server_cert_validation: ignore +ansible_password: "{{ windows_password }}" +ansible_become_password: "{{ admin_password }}" +ansible_connection: "winrm" +ansible_winrm_transport: "ntlm" +ansible_winrm_server_cert_validation: "ignore" +ansible_winrm_operation_timeout_sec: 120 +ansible_winrm_read_timeout_sec: 150 diff --git a/provisioner/provision_lab.yml b/provisioner/provision_lab.yml index 09f3c6d15..f0a305b6f 100644 --- a/provisioner/provision_lab.yml +++ b/provisioner/provision_lab.yml @@ -29,8 +29,8 @@ assert: that: - windows_password is defined - msg: "windows_password must be set for security automation workshop" - when: workshop_type == "security" + msg: "windows_password must be set for workshop_type: {{workshop_type}}" + when: workshop_type == "security" or workshop_type == "windows" - name: make sure network_type is set to a correct value assert: @@ -171,7 +171,7 @@ - connectivity_test - name: wait for all security nodes to have SSH reachability - hosts: "security_connection_check" + hosts: "attack:snort:qradar" become: true gather_facts: false roles: diff --git a/provisioner/roles/aws_workshop_login_page/templates/index.html.j2 b/provisioner/roles/aws_workshop_login_page/templates/index.html.j2 index 710925fa6..1f69662c5 100644 --- a/provisioner/roles/aws_workshop_login_page/templates/index.html.j2 +++ b/provisioner/roles/aws_workshop_login_page/templates/index.html.j2 @@ -454,7 +454,7 @@ To login to the Ansible Control Node use the following for SSH access:
    -
    ssh student{{number}}@sean_placeholder
    +
    ssh student{{number}}@{{ hostvars[ec2_name_prefix+ '-student' + number|string + '-ansible'].ansible_host }}
    {% if dns_type != 'none' %} DNS: student{{number}}.{{ec2_name_prefix}}.{{workshop_dns_zone}}
    ssh student{{number}}@student{{number}}.{{ec2_name_prefix}}.{{workshop_dns_zone}}
    @@ -477,7 +477,7 @@ To login to the Ansible Tower UI use the following credentials:
    {% if dns_type == 'none' %} UI link: -sean_placeholder +https://{{ hostvars[ec2_name_prefix+ '-student' + number|string + '-ansible'].ansible_host }} {% endif %} diff --git a/provisioner/roles/control_node/templates/inventory/devops.j2 b/provisioner/roles/control_node/templates/inventory/devops.j2 new file mode 100644 index 000000000..ebc54a5c2 --- /dev/null +++ b/provisioner/roles/control_node/templates/inventory/devops.j2 @@ -0,0 +1,22 @@ +[all:vars] +ansible_user={{username}} +ansible_password={{ admin_password }} +{% if ssh_port is defined %} +ansible_port={{ ssh_port }} +{% endif %} + +[sitea] +{% set node1 = ec2_name_prefix + '-' + username + '-node1' %} +{{ hostvars[node1].short_name }} ansible_host={{ hostvars[node1].private_ip }} +{% set node2 = ec2_name_prefix + '-' + username + '-node2' %} +{{ hostvars[node2].short_name }} ansible_host={{ hostvars[node2].private_ip }} + +[siteb] +{% set node3 = ec2_name_prefix + '-' + username + '-node3' %} +{{ hostvars[node3].short_name }} ansible_host={{ hostvars[node3].private_ip }} +{% set node4 = ec2_name_prefix + '-' + username + '-node4' %} +{{ hostvars[node4].short_name }} ansible_host={{ hostvars[node4].private_ip }} + +[control] +{% set control_node = ec2_name_prefix + '-' + username + '-ansible' %} +{{ hostvars[control_node].short_name }} ansible_host={{ hostvars[control_node].ansible_host }} diff --git a/provisioner/roles/control_node/templates/inventory/f5.j2 b/provisioner/roles/control_node/templates/inventory/f5.j2 new file mode 100644 index 000000000..6f04160be --- /dev/null +++ b/provisioner/roles/control_node/templates/inventory/f5.j2 @@ -0,0 +1,21 @@ +[all:vars] +ansible_user={{username}} +ansible_password={{ admin_password }} +{% if ssh_port is defined %} +ansible_port={{ ssh_port }} +{% endif %} + +[lb] +{% set f5_device = ec2_name_prefix + '-' + username + '-f5' %} +{{ hostvars[f5_device].short_name }} ansible_host={{ hostvars[f5_device].ansible_host }} ansible_user=admin private_ip={{ hostvars[f5_device].private_ip }} ansible_pass={{admin_password}} + +[control] +{% set control_node = ec2_name_prefix + '-' + username + '-ansible' %} +{{ hostvars[control_node].short_name }} ansible_host={{ hostvars[control_node].ansible_host }} + + +[web] +{% set node1 = ec2_name_prefix + '-' + username + '-node1' %} +{{ hostvars[node1].short_name }} ansible_host={{ hostvars[node1].ansible_host }} private_ip={{hostvars[node1].private_ip }} +{% set node2 = ec2_name_prefix + '-' + username + '-node2' %} +{{ hostvars[node2].short_name }} ansible_host={{ hostvars[node2].ansible_host }} private_ip={{hostvars[node2].private_ip }} diff --git a/provisioner/roles/control_node/templates/inventory/rhel.j2 b/provisioner/roles/control_node/templates/inventory/rhel.j2 new file mode 100644 index 000000000..fd146b195 --- /dev/null +++ b/provisioner/roles/control_node/templates/inventory/rhel.j2 @@ -0,0 +1,17 @@ +[all:vars] +ansible_user={{username}} +ansible_password={{ admin_password }} +{% if ssh_port is defined %} +ansible_port={{ ssh_port }} +{% endif %} + +[web] +{% for vm in groups[username] %} +{% if hostvars[vm].short_name != "ansible" %} +{{ hostvars[vm].short_name }} ansible_host={{ hostvars[vm].ansible_host }} +{% endif %} +{% endfor %} + +[control] +{% set control_node = ec2_name_prefix + '-' + username + '-ansible' %} +{{ hostvars[control_node].short_name }} ansible_host={{ hostvars[control_node].ansible_host }} diff --git a/provisioner/roles/control_node/templates/inventory/windows.j2 b/provisioner/roles/control_node/templates/inventory/windows.j2 index d32adc5b8..276a572b0 100644 --- a/provisioner/roles/control_node/templates/inventory/windows.j2 +++ b/provisioner/roles/control_node/templates/inventory/windows.j2 @@ -7,6 +7,8 @@ ansible_connection=winrm ansible_winrm_transport=credssp ansible_winrm_server_cert_validation=ignore ansible_port=5986 +ansible_user=Administrator +ansible_password="{{windows_password}}" [control_nodes:vars] ansible_port=22 @@ -15,10 +17,10 @@ ansible_ssh_private_key_file="{{ playbook_dir }}/{{ ec2_name_prefix }}/{{ ec2_na [{{ username }}] {{ username }}-ansible ansible_host={{ hostvars[control_node].ansible_host }} -{{ username }}-win1 ansible_host={{ hostvars[win1].ansible_host }} ansible_user=Administrator ansible_password="rSzVIZU*b$z@DwDsrrzubPezBm)j2-Cg" +{{ username }}-win1 ansible_host={{ hostvars[win1].ansible_host }} {% if doubleup %} {% set win2 = ec2_name_prefix + '-' + username + '-instance2' %} -{{ username }}-win2 ansible_host={{ hostvars[win2].ansible_host }} ansible_user=Administrator ansible_password="rSzVIZU*b$z@DwDsrrzubPezBm)j2-Cg" +{{ username }}-win2 ansible_host={{ hostvars[win2].ansible_host }} {% endif %} diff --git a/provisioner/roles/manage_ec2_instances/tasks/create_inventory.yml b/provisioner/roles/manage_ec2_instances/tasks/create_inventory.yml deleted file mode 100644 index 32ee30a7c..000000000 --- a/provisioner/roles/manage_ec2_instances/tasks/create_inventory.yml +++ /dev/null @@ -1,27 +0,0 @@ ---- -- name: grab facts for control_nodes - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_ansible": "{{ec2_name_prefix}}-ansible" - register: ansible_node_facts - -- name: add ansible node to control_nodes and managed_nodes group - add_host: - name: "{{ item.tags.Name }}" - username: "{{ item.tags.Student }}" - student: "{{ item.tags.Student }}" - short_name: "{{ item.tags.short_name }}" - ansible_host: "{{ item.public_ip_address }}" - ansible_user: "{{ item.tags.username }}" - ansible_port: "{{ ssh_port }}" - ansible_ssh_private_key_file: "{{ playbook_dir }}/{{ec2_name_prefix}}/{{ec2_name_prefix}}-private.pem" - private_ip: "{{item.private_ip_address}}" - groups: - - managed_nodes - - control_nodes - with_items: "{{ ansible_node_facts.instances }}" - -- name: setup inventory - include_tasks: "inventory/addhost_{{workshop_type}}.yml" diff --git a/provisioner/roles/manage_ec2_instances/tasks/instances/instances_networking.yml b/provisioner/roles/manage_ec2_instances/tasks/instances/instances_networking.yml index ce3bd149d..e06bb816c 100644 --- a/provisioner/roles/manage_ec2_instances/tasks/instances/instances_networking.yml +++ b/provisioner/roles/manage_ec2_instances/tasks/instances/instances_networking.yml @@ -44,6 +44,7 @@ ansible_network_os: "{{ec2_info[rtr1_type].os}}" username: "{{ec2_info[rtr1_type].username}}" launch_time: "{{item.1.launch_time}}" + group: core with_indexed_items: - "{{ rtr1_output.instances }}" when: rtr1_output.instance_ids is not none @@ -85,6 +86,7 @@ ansible_network_os: "{{ec2_info[rtr2_type].os}}" username: "{{ec2_info[rtr1_type].username}}" launch_time: "{{item.1.launch_time}}" + group: core with_indexed_items: - "{{ rtr2_output.instances }}" when: rtr2_output.instance_ids is not none @@ -126,6 +128,7 @@ ansible_network_os: "{{ec2_info[rtr3_type].os}}" username: "{{ec2_info[rtr1_type].username}}" launch_time: "{{item.1.launch_time}}" + group: access with_indexed_items: - "{{ rtr3_output.instances }}" when: rtr3_output.instance_ids is not none @@ -167,6 +170,7 @@ ansible_network_os: "{{ec2_info[rtr4_type].os}}" username: "{{ec2_info[rtr1_type].username}}" launch_time: "{{item.1.launch_time}}" + group: access with_indexed_items: - "{{ rtr4_output.instances }}" when: rtr4_output.instance_ids is not none diff --git a/provisioner/roles/manage_ec2_instances/tasks/instances/instances_windows.yml b/provisioner/roles/manage_ec2_instances/tasks/instances/instances_windows.yml index d6f381612..e98b2275c 100644 --- a/provisioner/roles/manage_ec2_instances/tasks/instances/instances_windows.yml +++ b/provisioner/roles/manage_ec2_instances/tasks/instances/instances_windows.yml @@ -41,7 +41,7 @@ Workshop_instance1: "{{ ec2_name_prefix }}-instance1" wait: "{{ ec2_wait }}" vpc_subnet_id: "{{ ec2_vpc_subnet_id }}" - user_data: "{{ lookup('template', 'skylight_windows_userdata.j2', template_vars=dict(vm_name='instance1')) }}" + user_data: "{{ lookup('template', 'windows_userdata.txt.j2', template_vars=dict(vm_name='instance1')) }}" register: instance1_output - name: WINDOWS | Create instance 2 @@ -57,7 +57,7 @@ Workshop_instance1: "{{ ec2_name_prefix }}-instance1" wait: "{{ ec2_wait }}" vpc_subnet_id: "{{ ec2_vpc_subnet_id }}" - user_data: "{{ lookup('template', 'skylight_windows_userdata.j2', template_vars=dict(vm_name='instance1')) }}" + user_data: "{{ lookup('template', 'windows_userdata.txt.j2', template_vars=dict(vm_name='instance1')) }}" register: instance2_output when: doubleup|bool diff --git a/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_devops.yml b/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_devops.yml deleted file mode 100644 index ff7c3b9ab..000000000 --- a/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_devops.yml +++ /dev/null @@ -1,83 +0,0 @@ ---- -- name: grab facts for node1 - dev_web1 - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop": "{{ec2_name_prefix}}" - "tag:Devops_tag": "dev_web1" - register: node1_node_facts - -- name: grab facts for node2 node - dev_web2 - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop": "{{ec2_name_prefix}}" - "tag:Devops_tag": "dev_web2" - register: node2_node_facts - -- name: grab facts for node3 node - prod_web1 - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop": "{{ec2_name_prefix}}" - "tag:Devops_tag": "prod_web1" - register: node3_node_facts - -- name: grab facts for node4 node - prod_web2 - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop": "{{ec2_name_prefix}}" - "tag:Devops_tag": "prod_web2" - register: node4_node_facts - -- name: add hosts to site1 group - add_host: - name: "{{ item.tags.Name }}" - username: "{{ item.tags.Student }}" - short_name: "{{ item.tags.short_name }}" - ansible_host: "{{ item.public_ip_address }}" - ansible_user: "{{ item.tags.username }}" - ansible_port: "{{ ssh_port }}" - ansible_ssh_private_key_file: "{{ playbook_dir }}/{{ec2_name_prefix}}/{{ec2_name_prefix}}-private.pem" - groups: lab_hosts,managed_nodes, site1 - with_items: - - "{{ node1_node_facts.instances }}" - - "{{ node2_node_facts.instances }}" - changed_when: false - -- name: add hosts to site2 group - add_host: - name: "{{ item.tags.Name }}" - username: "{{ item.tags.Student }}" - short_name: "{{ item.tags.short_name }}" - ansible_host: "{{ item.public_ip_address }}" - ansible_user: "{{ item.tags.username }}" - ansible_port: "{{ ssh_port }}" - ansible_ssh_private_key_file: "{{ playbook_dir }}/{{ec2_name_prefix}}/{{ec2_name_prefix}}-private.pem" - groups: lab_hosts,managed_nodes, site2 - with_items: - - "{{ node3_node_facts.instances }}" - - "{{ node4_node_facts.instances }}" - changed_when: false - -- name: Generate student inventories - template: - src: devops_instances.txt.j2 - dest: "{{ playbook_dir }}/{{ec2_name_prefix}}/student{{item}}-instances.txt" - with_sequence: count="{{ student_total }}" - -- name: Generate student etchosts - template: - src: etchosts_devops.j2 - dest: "{{ playbook_dir }}/{{ec2_name_prefix}}/student{{item}}-etchosts.txt" - with_sequence: count="{{ student_total }}" - -- name: Generate instructor inventory - template: - src: instructor_inventory_devops.j2 - dest: "{{ playbook_dir }}/{{ec2_name_prefix}}/instructor_inventory.txt" diff --git a/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_f5.yml b/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_f5.yml deleted file mode 100644 index 6d884e4a7..000000000 --- a/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_f5.yml +++ /dev/null @@ -1,85 +0,0 @@ ---- -- name: grab facts for f5 node (F5 MODE) - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_f5": "{{ec2_name_prefix}}-f5" - register: f5_node_facts - -- name: add f5 to groups (F5 MODE) - add_host: - name: "{{ item.tags.Name }}" - short_name: "{{ item.tags.short_name }}" - ansible_host: "{{ item.public_ip_address }}" - ansible_user: admin - ansible_password: admin - ansible_port: "{{ ssh_port }}" - ansible_ssh_private_key_file: "{{ playbook_dir }}/{{ec2_name_prefix}}/{{ec2_name_prefix}}-private.pem" - private_ip: "{{item.private_ip_address}}" - groups: - - f5 - with_items: "{{ f5_node_facts.instances }}" - -- name: grab facts for node1 node (F5 MODE) - ec2_instance_facts: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_node1": "{{ec2_name_prefix}}-node1" - register: node1_node_facts - -- name: add node1 to groups (F5 MODE) - add_host: - name: "{{ item.tags.Name }}" - short_name: "{{ item.tags.short_name }}" - username: "{{ item.tags.Student }}" - ansible_host: "{{ item.public_ip_address }}" - ansible_user: "{{ item.tags.username }}" - ansible_port: "{{ ssh_port }}" - ansible_ssh_private_key_file: "{{ playbook_dir }}/{{ec2_name_prefix}}/{{ec2_name_prefix}}-private.pem" - private_ip: "{{item.private_ip_address}}" - groups: - - lab_hosts - - managed_nodes - with_items: "{{ node1_node_facts.instances }}" - -- name: grab facts for node2 node (F5 MODE) - ec2_instance_facts: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_node2": "{{ec2_name_prefix}}-node2" - register: node2_node_facts - -- name: add node2 to groups (F5 MODE) - add_host: - name: "{{ item.tags.Name }}" - short_name: "{{ item.tags.short_name }}" - username: "{{ item.tags.Student }}" - ansible_host: "{{ item.public_ip_address }}" - ansible_user: "{{ item.tags.username }}" - ansible_port: "{{ ssh_port }}" - ansible_ssh_private_key_file: "{{ playbook_dir }}/{{ec2_name_prefix}}/{{ec2_name_prefix}}-private.pem" - private_ip: "{{item.private_ip_address}}" - groups: - - lab_hosts - - managed_nodes - with_items: "{{ node2_node_facts.instances }}" - -- name: Generate student inventories - template: - src: f5_instances.txt.j2 - dest: "{{ playbook_dir }}/{{ec2_name_prefix}}/student{{item}}-instances.txt" - with_sequence: count="{{ student_total }}" - -- name: Generate student etchosts - template: - src: etchosts_f5.j2 - dest: "{{ playbook_dir }}/{{ec2_name_prefix}}/student{{item}}-etchosts.txt" - with_sequence: count="{{ student_total }}" - -- name: Generate instructor inventory - template: - src: instructor_inventory_f5.j2 - dest: "{{ playbook_dir }}/{{ec2_name_prefix}}/instructor_inventory.txt" diff --git a/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_networking.yml b/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_networking.yml deleted file mode 100644 index 5af3e49ee..000000000 --- a/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_networking.yml +++ /dev/null @@ -1,114 +0,0 @@ ---- -- name: grab facts for rtr1 node (NETWORKING MODE) - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_rtr1": "{{ec2_name_prefix}}-rtr1" - register: rtr1_node_facts - -- name: grab facts for rtr2 node (NETWORKING MODE) - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_rtr2": "{{ec2_name_prefix}}-rtr2" - register: rtr2_node_facts - -- name: grab facts for rtr3 node (NETWORKING MODE) - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_rtr3": "{{ec2_name_prefix}}-rtr3" - register: rtr3_node_facts - -- name: grab facts for rtr4 node (NETWORKING MODE) - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_rtr4": "{{ec2_name_prefix}}-rtr4" - register: rtr4_node_facts - -- name: ADD RTR1 ROUTERS TO INVENTORY - add_host: - name: "{{ item.tags.Name }}" - short_name: "{{ item.tags.short_name }}" - ansible_host: "{{ item.public_ip_address }}" - username: "{{ item.tags.Student }}" - ansible_user: "{{ item.tags.username }}" - ansible_port: "{{ ssh_port }}" - ansible_ssh_private_key_file: "{{ playbook_dir }}/{{ec2_name_prefix}}/{{ec2_name_prefix}}-private.pem" - private_ip: "{{item.private_ip_address}}" - ansible_network_os: "{{item.tags.ansible_network_os}}" - groups: - - routers - - core - - dmvpn - loop: "{{ rtr1_node_facts.instances|flatten(levels=1) }}" - -- name: ADD RTR2 ROUTERS TO INVENTORY - add_host: - name: "{{ item.tags.Name }}" - short_name: "{{ item.tags.short_name }}" - ansible_host: "{{ item.public_ip_address }}" - username: "{{ item.tags.Student }}" - ansible_user: "{{ item.tags.username }}" - ansible_port: "{{ ssh_port }}" - ansible_ssh_private_key_file: "{{ playbook_dir }}/{{ec2_name_prefix}}/{{ec2_name_prefix}}-private.pem" - private_ip: "{{item.private_ip_address}}" - ansible_network_os: "{{item.tags.ansible_network_os}}" - groups: - - routers - - core - loop: "{{ rtr2_node_facts.instances|flatten(levels=1) }}" - -- name: ADD RTR3 ROUTERS TO INVENTORY - add_host: - name: "{{ item.tags.Name }}" - short_name: "{{ item.tags.short_name }}" - ansible_host: "{{ item.public_ip_address }}" - username: "{{ item.tags.Student }}" - ansible_user: "{{ item.tags.username }}" - ansible_port: "{{ ssh_port }}" - ansible_ssh_private_key_file: "{{ playbook_dir }}/{{ec2_name_prefix}}/{{ec2_name_prefix}}-private.pem" - private_ip: "{{item.private_ip_address}}" - ansible_network_os: "{{item.tags.ansible_network_os}}" - groups: - - routers - - access - loop: "{{ rtr3_node_facts.instances|flatten(levels=1) }}" - -- name: ADD RTR3 ROUTERS TO INVENTORY - add_host: - name: "{{ item.tags.Name }}" - short_name: "{{ item.tags.short_name }}" - ansible_host: "{{ item.public_ip_address }}" - username: "{{ item.tags.Student }}" - ansible_user: "{{ item.tags.username }}" - ansible_port: "{{ ssh_port }}" - ansible_ssh_private_key_file: "{{ playbook_dir }}/{{ec2_name_prefix}}/{{ec2_name_prefix}}-private.pem" - private_ip: "{{item.private_ip_address}}" - ansible_network_os: "{{item.tags.ansible_network_os}}" - groups: - - routers - - access - loop: "{{ rtr4_node_facts.instances|flatten(levels=1) }}" - -- name: Generate student inventories - template: - src: networking_instances.txt.j2 - dest: "{{ playbook_dir }}/{{ec2_name_prefix}}/student{{item}}-instances.txt" - with_sequence: count="{{ student_total }}" - -- name: Generate student etchosts - template: - src: etchosts_networking.j2 - dest: "{{ playbook_dir }}/{{ec2_name_prefix}}/student{{item}}-etchosts.txt" - with_sequence: count="{{ student_total }}" - -- name: Generate instructor inventory - template: - src: instructor_inventory_networking.j2 - dest: "{{ playbook_dir }}/{{ec2_name_prefix}}/instructor_inventory.txt" diff --git a/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_rhel.yml b/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_rhel.yml deleted file mode 100644 index 890a5b298..000000000 --- a/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_rhel.yml +++ /dev/null @@ -1,57 +0,0 @@ ---- -- name: grab facts for node1 node - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_node1": "{{ec2_name_prefix}}-node1" - register: node1_node_facts - -- name: grab facts for node2 node - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_node2": "{{ec2_name_prefix}}-node2" - register: node2_node_facts - -- name: grab facts for node3 node - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_node3": "{{ec2_name_prefix}}-node3" - register: node3_node_facts - -- name: add hosts to groups (ANSIBLE RHEL MODE) - add_host: - name: "{{ item.tags.Name }}" - username: "{{ item.tags.Student }}" - short_name: "{{ item.tags.short_name }}" - ansible_host: "{{ item.public_ip_address }}" - ansible_user: "{{ item.tags.username }}" - ansible_port: "{{ ssh_port }}" - ansible_ssh_private_key_file: "{{ playbook_dir }}/{{ec2_name_prefix}}/{{ec2_name_prefix}}-private.pem" - groups: lab_hosts,managed_nodes - with_items: - - "{{ node1_node_facts.instances }}" - - "{{ node2_node_facts.instances }}" - - "{{ node3_node_facts.instances }}" - changed_when: false - -- name: Generate student inventories - template: - src: instances.txt.j2 - dest: "{{ playbook_dir }}/{{ec2_name_prefix}}/student{{item}}-instances.txt" - with_sequence: count="{{ student_total }}" - -- name: Generate student etchosts - template: - src: etchosts_rhel.j2 - dest: "{{ playbook_dir }}/{{ec2_name_prefix}}/student{{item}}-etchosts.txt" - with_sequence: count="{{ student_total }}" - -- name: Generate instructor inventory - template: - src: instructor_inventory.j2 - dest: "{{ playbook_dir }}/{{ec2_name_prefix}}/instructor_inventory.txt" diff --git a/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_rhel_90.yml b/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_rhel_90.yml deleted file mode 100644 index b3c3a2054..000000000 --- a/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_rhel_90.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -## re-use RHEL workshop code -- name: provision aws resources - include_tasks: addhost_rhel.yml diff --git a/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_security.yml b/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_security.yml deleted file mode 100644 index 7733ec255..000000000 --- a/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_security.yml +++ /dev/null @@ -1,218 +0,0 @@ ---- -- name: MANAGE SPLUNK EC2 INSTANCES (SECURITY MODE) - when: security_console == 'splunk' - block: - - name: grab facts for splunknode (SECURITY MODE) - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_splunk": "{{ec2_name_prefix}}-splunk" - register: splunk_node_facts - - - name: ADD SPLUNK NODE TO INVENTORY - add_host: - name: "{{ item.tags.Name }}" - short_name: "{{ item.tags.short_name }}" - ansible_host: "{{ item.public_ip_address }}" - username: "{{ item.tags.Student }}" - ansible_user: "{{ item.tags.username }}" - ansible_port: "{{ ssh_port }}" - ansible_ssh_private_key_file: "{{ playbook_dir }}/{{ec2_name_prefix}}/{{ec2_name_prefix}}-private.pem" - ansible_python_interpreter: "{{ec2_info['splunk_enterprise']['python_interpreter']}}" - private_ip: "{{item.private_ip_address}}" - groups: - - security - - security_connection_check - - splunk - - siem - loop: "{{ splunk_node_facts.instances|flatten(levels=1) }}" - -- name: MANAGE QRADAR EC2 INSTANCES (SECURITY MODE) - when: security_console == 'qradar' - block: - - name: grab facts for qradarnode (SECURITY MODE) - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_qradar": "{{ec2_name_prefix}}-qradar" - register: qradar_node_facts - - - name: ADD QRADAR NODE TO INVENTORY - add_host: - name: "{{ item.tags.Name }}" - short_name: "{{ item.tags.short_name }}" - ansible_host: "{{ item.public_ip_address }}" - username: "{{ item.tags.Student }}" - ansible_user: "{{ item.tags.username }}" - ansible_port: "{{ ssh_port }}" - ansible_ssh_private_key_file: "{{ playbook_dir }}/{{ec2_name_prefix}}/{{ec2_name_prefix}}-private.pem" - ansible_python_interpreter: "{{ec2_info['qradar']['python_interpreter']}}" - private_ip: "{{item.private_ip_address}}" - groups: - - security - - security_connection_check - - qradar - - siem - loop: "{{ qradar_node_facts.instances|flatten(levels=1) }}" - -- name: MANAGE ATTACK SIMULATION EC2 INSTANCES (SECURITY MODE) - block: - - name: grab facts for attack simulation node (SECURITY MODE) - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_attacker": "{{ec2_name_prefix}}-attacker" - register: attacker_node_facts - - - name: ADD ATTACK SIMULATION NODE TO INVENTORY - add_host: - name: "{{ item.tags.Name }}" - short_name: "{{ item.tags.short_name }}" - ansible_host: "{{ item.public_ip_address }}" - username: "{{ item.tags.Student }}" - ansible_user: "{{ item.tags.username }}" - ansible_port: "{{ ssh_port }}" - ansible_ssh_private_key_file: "{{ playbook_dir }}/{{ec2_name_prefix}}/{{ec2_name_prefix}}-private.pem" - private_ip: "{{ item['network_interfaces']|map(attribute='private_ip_address')|list|ipaddr('172.16.0.0/16')|first }}" - private_ip2: "{{ item['network_interfaces']|map(attribute='private_ip_address')|list|ipaddr('172.17.0.0/16')|first }}" - groups: - - security - - security_connection_check - - attack - loop: "{{ attacker_node_facts.instances|flatten(levels=1) }}" - -- name: grab facts for Check Point CloudGuard Security Management (SECURITY MODE) - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_checkpoint_gw": "{{ec2_name_prefix}}-checkpoint" - register: checkpoint_gw_facts - -- name: MANAGE SNORT EC2 INSTANCES (SECURITY MODE) - block: - - name: grab facts for snort node (SECURITY MODE) - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_snort": "{{ec2_name_prefix}}-snort" - register: snort_node_facts - - - name: ADD SNORT NODE TO INVENTORY - add_host: - name: "{{ item.tags.Name }}" - short_name: "{{ item.tags.short_name }}" - ansible_host: "{{ item.public_ip_address }}" - username: "{{ item.tags.Student }}" - ansible_user: "{{ item.tags.username }}" - ansible_port: "{{ ssh_port }}" - ansible_ssh_private_key_file: "{{ playbook_dir }}/{{ec2_name_prefix}}/{{ec2_name_prefix}}-private.pem" - private_ip: "{{ item['network_interfaces']|map(attribute='private_ip_address')|list|ipaddr('172.16.0.0/16')|first }}" - private_ip2: "{{ item['network_interfaces']|map(attribute='private_ip_address')|list|ipaddr('172.17.0.0/16')|first }}" - groups: - - security - - security_connection_check - - snort - - ids - loop: "{{ snort_node_facts.instances|flatten(levels=1) }}" - -- name: MANAGE CHECK POINT EC2 INSTANCES (SECURITY MODE) - block: - - name: grab facts for Check Point CloudGuard Security Management (SECURITY MODE) - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_checkpoint_mgmt": "{{ec2_name_prefix}}-checkpoint" - register: checkpoint_node_facts - - - name: ADD Check Point CloudGuard Security Management TO INVENTORY - add_host: - name: "{{ item.tags.Name }}" - short_name: "{{ item.tags.short_name }}" - ansible_host: "{{ item.public_ip_address }}" - username: "{{ item.tags.Student }}" - ansible_user: "{{ item.tags.username }}" - ansible_port: "{{ ssh_port }}" - ansible_ssh_private_key_file: "{{ playbook_dir }}/{{ec2_name_prefix}}/{{ec2_name_prefix}}-private.pem" - private_ip: "{{item.private_ip_address}}" - groups: - - security - - checkpoint - - firewall - - checkpoint_mgmt - loop: "{{ checkpoint_node_facts.instances|flatten(levels=1) }}" - - - name: grab facts for Check Point NGFW (SECURITY MODE) - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_checkpoint_gw": "{{ec2_name_prefix}}-checkpoint" - register: checkpoint_gw_node_facts - - - name: ADD Check Point NGFW TO INVENTORY - add_host: - name: "{{ item.tags.Name }}" - short_name: "{{ item.tags.short_name }}" - ansible_host: "{{ item.public_ip_address }}" - username: "{{ item.tags.Student }}" - ansible_user: "{{ item.tags.username }}" - ansible_port: "{{ ssh_port }}" - ansible_ssh_private_key_file: "{{ playbook_dir }}/{{ec2_name_prefix}}/{{ec2_name_prefix}}-private.pem" - private_ip: "{{ item['network_interfaces']|map(attribute='private_ip_address')|list|ipaddr('172.16.0.0/16')|first }}" - private_ip2: "{{ item['network_interfaces']|map(attribute='private_ip_address')|list|ipaddr('172.17.0.0/16')|first }}" - groups: - - security - - checkpoint - - firewall - loop: "{{ checkpoint_gw_node_facts['instances']|flatten(levels=1) }}" - -- name: MANAGE WINDOWS EC2 INSTANCES (SECURITY MODE) - block: - - name: grab facts for Windows workstation for Check Point (SECURITY MODE) - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_windows": "{{ec2_name_prefix}}-windows" - register: windows_node_facts - - - name: ADD WINDOWS WORKSTATION TO INVENTORY - add_host: - name: "{{ item.tags.Name }}" - short_name: "{{ item.tags.short_name }}" - ansible_host: "{{ item.public_ip_address }}" - ansible_user: Administrator - ansible_password: "{{ windows_password }}" - ansible_port: 5986 - ansible_connection: winrm - ansible_winrm_server_cert_validation: ignore - username: "{{ item.tags.Student }}" - private_ip: "{{item.private_ip_address}}" - groups: - - security - - windows - - workstation - loop: "{{ windows_node_facts.instances|flatten(levels=1) }}" - -- name: Generate student inventories - template: - src: security_instances.txt.j2 - dest: "{{ playbook_dir }}/{{ec2_name_prefix}}/student{{item}}-instances.txt" - with_sequence: count="{{ student_total }}" - -- name: Generate student etchosts - template: - src: etchosts_security.j2 - dest: "{{ playbook_dir }}/{{ec2_name_prefix}}/student{{item}}-etchosts.txt" - with_sequence: count="{{ student_total }}" - -- name: Generate instructor inventory - template: - src: instructor_inventory_security.j2 - dest: "{{ playbook_dir }}/{{ec2_name_prefix}}/instructor_inventory.txt" diff --git a/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_windows.yml b/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_windows.yml deleted file mode 100644 index 508308e6f..000000000 --- a/provisioner/roles/manage_ec2_instances/tasks/inventory/addhost_windows.yml +++ /dev/null @@ -1,139 +0,0 @@ ---- -################### Get workshop nodes info ################### - -- name: grab facts for Gitlab - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_gitlab": "{{ec2_name_prefix}}-gitlab" - register: gitlab_node_facts - -- name: grab facts for instance 1 - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_instance1": "{{ec2_name_prefix}}-instance1" - register: instance1_node_facts - -- name: grab facts for instance 2 - ec2_instance_info: - region: "{{ ec2_region }}" - filters: - instance-state-name: running - "tag:Workshop_instance2": "{{ec2_name_prefix}}-instance2" - register: instance2_node_facts - when: - - doubleup|bool - -################### Get Passwords ################### - -- name: Windows | Obtain windows passwords for instance1 - ec2_win_password: - region: '{{ ec2_region }}' - instance_id: "{{ item.instance_id }}" - key_file: "{{ playbook_dir }}/{{ ec2_name_prefix }}/{{ ec2_name_prefix }}-private.pem" - wait: true - wait_timeout: 300 - with_items: "{{ instance1_node_facts.instances }}" - register: ec2windows1_password - when: instance1_node_facts is defined - -- name: Windows | Obtain windows passwords for instance2 - ec2_win_password: - region: '{{ ec2_region }}' - instance_id: "{{ item.instance_id }}" - key_file: "{{ playbook_dir }}/{{ ec2_name_prefix }}/{{ ec2_name_prefix }}-private.pem" - wait: true - wait_timeout: 300 - with_items: "{{ instance2_node_facts.instances }}" - register: ec2windows2_password - when: - - doubleup|bool - - instance2_node_facts is defined - -################### Create inventories ################### - -- name: GitLab | Add new instance to host group - add_host: - hostname: 'gitlab' - short_name: "{{ item.tags.short_name }}" - username: "{{ item.tags.username }}" - ansible_user: "{{ item.tags.username }}" - ansible_ssh_private_key_file: "{{ playbook_dir }}/{{ ec2_name_prefix }}/{{ ec2_name_prefix }}-private.pem" - ansible_host: "{{ item.public_ip_address }}" - private_ip: "{{ item.private_ip_address }}" - groups: - - rhel - - gitlab - with_items: "{{ gitlab_node_facts.instances }}" - -- name: Windows instance1 | Add new instance to host group - add_host: - hostname: "student{{ item.item.tags.Student | regex_replace('[^0-9]', '') }}-win1" - short_name: "{{ item.item.tags.short_name }}" - ansible_host: "{{ item.item.public_ip_address }}" - private_ip: "{{ item.item.private_ip_address }}" - ansible_port: 5986 - ansible_user: "Administrator" - ansible_password: "{{ item.win_password }}" - ansible_become_password: "{{ admin_password }}" - ansible_connection: "winrm" - ansible_winrm_transport: "ntlm" - ansible_winrm_server_cert_validation: "ignore" - ansible_winrm_operation_timeout_sec: 120 - ansible_winrm_read_timeout_sec: 150 - student: "{{ item.item.tags.Student | regex_replace('[^0-9]', '') }}" - groups: - - windows - with_items: "{{ ec2windows1_password.results }}" - when: ec2windows1_password is defined - -- name: Windows instance2 | Add new instance to host group - add_host: - hostname: "student{{ item.item.tags.Student | regex_replace('[^0-9]', '') }}-win2" - short_name: "{{ item.item.tags.short_name }}" - ansible_host: "{{ item.item.public_ip_address }}" - private_ip: "{{ item.item.private_ip_address }}" - ansible_port: 5986 - ansible_user: "Administrator" - ansible_password: "{{ item.win_password }}" - ansible_become_password: "{{ admin_password }}" - ansible_connection: "winrm" - ansible_winrm_transport: "ntlm" - ansible_winrm_server_cert_validation: "ignore" - ansible_winrm_operation_timeout_sec: 120 - ansible_winrm_read_timeout_sec: 150 - student: "{{ item.item.tags.Student | regex_replace('[^0-9]', '') }}" - groups: - - windows - with_items: "{{ ec2windows2_password.results }}" - when: - - doubleup|bool - - ec2windows2_password is defined - -- name: Windows | Wait for WinRM to come up - wait_for_connection: - delegate_to: "{{ item }}" - with_items: - - "{{ groups['windows'] }}" - -################### Write inventories ################### - -- name: Generate instructor inventory - template: - src: instructor_inventory_windows.j2 - dest: "{{ playbook_dir }}/{{ ec2_name_prefix }}/instructor_inventory.txt" - -- name: Generate student etchosts - template: - src: etchosts_windows.j2 - dest: "{{ playbook_dir }}/{{ec2_name_prefix}}/student{{item}}-etchosts.txt" - with_sequence: count="{{ student_total }}" - -- name: Generate student inventories - template: - src: windows_instances.txt.j2 - dest: "{{ playbook_dir }}/{{ ec2_name_prefix }}/student{{ item }}-instances.txt" - with_sequence: count="{{ student_total }}" diff --git a/provisioner/roles/manage_ec2_instances/tasks/inventory/dynamic.yml b/provisioner/roles/manage_ec2_instances/tasks/inventory/dynamic.yml index e9ee11f0f..9020f2c1c 100644 --- a/provisioner/roles/manage_ec2_instances/tasks/inventory/dynamic.yml +++ b/provisioner/roles/manage_ec2_instances/tasks/inventory/dynamic.yml @@ -11,12 +11,19 @@ - name: refresh AWS inventory information meta: refresh_inventory -- name: debug it +- group_by: + key: security_connection_check + parents: + - attack + - snort + - qradar + when: workshop_type == "security" + +- name: display all groups from dynamic inventory debug: msg: "{{ groups }}" - -# - name: Generate instructor inventory -# template: -# src: instructor_inventory.j2 -# dest: "{{ playbook_dir }}/{{ec2_name_prefix}}/instructor_inventory.txt" +- name: Generate instructor inventory + template: + src: instructor_inv.j2 + dest: "{{ playbook_dir }}/{{ec2_name_prefix}}/instructor_inventory.txt" diff --git a/provisioner/roles/manage_ec2_instances/tasks/provision.yml b/provisioner/roles/manage_ec2_instances/tasks/provision.yml index 04a732d07..d9b379e5a 100644 --- a/provisioner/roles/manage_ec2_instances/tasks/provision.yml +++ b/provisioner/roles/manage_ec2_instances/tasks/provision.yml @@ -78,3 +78,7 @@ ## Instance creation - name: provision workshop instances include_tasks: 'instances/instances_{{workshop_type}}.yml' + +## inventory for subsequent roles to configure workshop node +- name: provision workshop instances + include_tasks: '{{role_path}}/tasks/inventory/dynamic.yml' diff --git a/provisioner/roles/manage_ec2_instances/tasks/security_includes/security_ec2_create_instances.yml b/provisioner/roles/manage_ec2_instances/tasks/security_includes/security_ec2_create_instances.yml index 6ffc5f984..b4897ebc2 100644 --- a/provisioner/roles/manage_ec2_instances/tasks/security_includes/security_ec2_create_instances.yml +++ b/provisioner/roles/manage_ec2_instances/tasks/security_includes/security_ec2_create_instances.yml @@ -123,7 +123,7 @@ image: "{{ec2_info.windows_ws.ami.image_id}}" region: "{{ ec2_region }}" exact_count: "{{ student_total }}" - user_data: "{{ lookup('template', 'windows_userdata.txt.j2') }}" + user_data: "{{ lookup('template', 'windows_userdata.txt.j2', template_vars=dict(vm_name='windows_ws')) }}" count_tag: Workshop_windows: "{{ec2_name_prefix}}-windows" wait: "{{ ec2_wait }}" diff --git a/provisioner/roles/manage_ec2_instances/templates/aws_ec2.j2 b/provisioner/roles/manage_ec2_instances/templates/aws_ec2.j2 new file mode 100644 index 000000000..ba5a25e08 --- /dev/null +++ b/provisioner/roles/manage_ec2_instances/templates/aws_ec2.j2 @@ -0,0 +1,42 @@ +--- +plugin: aws_ec2 +regions: + - "{{ec2_region}}" +filters: + tag:Workshop: "{{ec2_name_prefix}}" +groups: + # all workshop types + control_nodes: "'ansible' in tags.short_name" + # rhel, rhel_90, f5 + lab_hosts: "'node' in tags.short_name" + # network + routers: "'rtr' in tags.short_name" + core: "'core' in tags.group" + access: "'access' in tags.group" + # f5 + # windows + windows: "'instance' in tags.short_name" + gitlab: "'gitlab' in tags.short_name" + # security + qradar: "'qradar' in tags.short_name" + attack: "'attack' in tags.short_name" + security: "'security' in tags.Workshop_type" + security_connection_check: attack snort qradar + snort: "'snort' in tags.short_name" + checkpoint: "'checkpoint_gw' in tags.short_name" + checkpoint_mgmt: "'checkpoint_mgmt' in tags.short_name" + security_windows: "'windows_ws' in tags.short_name" +{% for n in range(1, student_total + 1) %} + student{{ n }}: "'student{{ n }}' == tags.Student" +{% endfor %} +hostnames: + - tag:Name +compose: + ansible_host: public_ip_address + ansible_user: tags.username + short_name: tags.short_name + username: tags.Student + ansible_port: "{{ssh_port}}" + ansible_ssh_private_key_file: '"{{ playbook_dir }}/{{ec2_name_prefix}}/{{ec2_name_prefix}}-private.pem"' + private_ip: private_ip_address + ansible_network_os: tags.ansible_network_os diff --git a/provisioner/roles/manage_ec2_instances/templates/instructor_inv.j2 b/provisioner/roles/manage_ec2_instances/templates/instructor_inv.j2 index e69de29bb..14e6edb3d 100644 --- a/provisioner/roles/manage_ec2_instances/templates/instructor_inv.j2 +++ b/provisioner/roles/manage_ec2_instances/templates/instructor_inv.j2 @@ -0,0 +1,20 @@ +[all:vars] +ansible_ssh_private_key_file={{ playbook_dir }}/{{ ec2_name_prefix }}/{{ ec2_name_prefix }}-private.pem +{% if ssh_port is defined %} +ansible_port={{ ssh_port }} +{% endif %} + + +{% for number in range(1,student_total + 1) %} + +[student{{ number }}] +{% for vm in groups['student' + number|string] %} +{{ hostvars[vm].username }}-{{ hostvars[vm].short_name }} ansible_host={{ hostvars[vm].ansible_host }} ansible_user={{ hostvars[vm].ansible_user }} +{% endfor %} + +{% endfor %} + +[control] +{% for vm in groups.control_nodes %} +{{ hostvars[vm].username }}-{{ hostvars[vm].short_name }} ansible_host={{ hostvars[vm].ansible_host }} ansible_user={{ hostvars[vm].ansible_user }} +{% endfor %} diff --git a/provisioner/roles/manage_ec2_instances/templates/instructor_inventory.j2 b/provisioner/roles/manage_ec2_instances/templates/instructor_inventory.j2 deleted file mode 100644 index 0468d0ec9..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/instructor_inventory.j2 +++ /dev/null @@ -1,28 +0,0 @@ -[all:vars] -{% if ssh_port is defined %} -ansible_port={{ ssh_port }} -{% endif %} - -{% for number in range(1,student_total + 1) %} -[student{{ number }}] -{% for host in ansible_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{ host.tags.Name | regex_replace(ec2_name_prefix ~ '-','') }} ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} -{% endif %} -{% endfor %} -{% for host in node1_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{ host.tags.Name | regex_replace(ec2_name_prefix ~ '-','') }} ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} -{% endif %} -{% endfor %} -{% for host in node2_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{ host.tags.Name | regex_replace(ec2_name_prefix ~ '-','') }} ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} -{% endif %} -{% endfor %} -{% for host in node3_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{ host.tags.Name | regex_replace(ec2_name_prefix ~ '-','') }} ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} -{% endif %} -{% endfor %} -{% endfor %} diff --git a/provisioner/roles/manage_ec2_instances/templates/instructor_inventory_devops.j2 b/provisioner/roles/manage_ec2_instances/templates/instructor_inventory_devops.j2 deleted file mode 100644 index b2a00f3ee..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/instructor_inventory_devops.j2 +++ /dev/null @@ -1,33 +0,0 @@ -[all:vars] -{% if ssh_port is defined %} -ansible_port={{ ssh_port }} -{% endif %} - -{% for number in range(1,student_total + 1) %} -[student{{ number }}] -{% for host in ansible_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{ host.tags.Name | regex_replace(ec2_name_prefix ~ '-','') }} ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} -{% endif %} -{% endfor %} -{% for host in node1_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{ host.tags.Name | regex_replace(ec2_name_prefix ~ '-','') }} ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} -{% endif %} -{% endfor %} -{% for host in node2_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{ host.tags.Name | regex_replace(ec2_name_prefix ~ '-','') }} ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} -{% endif %} -{% endfor %} -{% for host in node3_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{ host.tags.Name | regex_replace(ec2_name_prefix ~ '-','') }} ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} -{% endif %} -{% endfor %} -{% for host in node4_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{ host.tags.Name | regex_replace(ec2_name_prefix ~ '-','') }} ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} -{% endif %} -{% endfor %} -{% endfor %} diff --git a/provisioner/roles/manage_ec2_instances/templates/instructor_inventory_f5.j2 b/provisioner/roles/manage_ec2_instances/templates/instructor_inventory_f5.j2 deleted file mode 100644 index 2d5ade532..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/instructor_inventory_f5.j2 +++ /dev/null @@ -1,28 +0,0 @@ -[all:vars] -{% if ssh_port is defined %} -ansible_port={{ ssh_port }} -{% endif %} - -{% for number in range(1,student_total + 1) %} -[student{{ number }}] -{% for host in ansible_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{host.tags.Student}}-ansible ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} -{% endif %} -{% endfor %} -{% for host in f5_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{host.tags.Student}}-f5 ansible_host={{ host.public_ip_address }} ansible_user=admin -{% endif %} -{% endfor %} -{% for host in node1_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{host.tags.Student}}-node1 ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} -{% endif %} -{% endfor %} -{% for host in node2_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{host.tags.Student}}-node2 ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} -{% endif %} -{% endfor %} -{% endfor %} diff --git a/provisioner/roles/manage_ec2_instances/templates/instructor_inventory_networking.j2 b/provisioner/roles/manage_ec2_instances/templates/instructor_inventory_networking.j2 deleted file mode 100644 index 2e4000ba8..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/instructor_inventory_networking.j2 +++ /dev/null @@ -1,33 +0,0 @@ -[all:vars] -{% if ssh_port is defined %} -ansible_port={{ ssh_port }} -{% endif %} - -{% for number in range(1,student_total + 1) %} -[student{{ number }}] -{% for host in ansible_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{ host.tags.Name | regex_replace(ec2_name_prefix ~ '-','') }} ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} -{% endif %} -{% endfor %} -{% for host in rtr1_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{ host.tags.Name | regex_replace(ec2_name_prefix ~ '-','') }} ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} ansible_network_os={{ host.tags.ansible_network_os }} -{% endif %} -{% endfor %} -{% for host in rtr2_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{ host.tags.Name | regex_replace(ec2_name_prefix ~ '-','') }} ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} ansible_network_os={{ host.tags.ansible_network_os }} -{% endif %} -{% endfor %} -{% for host in rtr3_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{ host.tags.Name | regex_replace(ec2_name_prefix ~ '-','') }} ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} ansible_network_os={{ host.tags.ansible_network_os }} -{% endif %} -{% endfor %} -{% for host in rtr4_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{ host.tags.Name | regex_replace(ec2_name_prefix ~ '-','') }} ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} ansible_network_os={{ host.tags.ansible_network_os }} -{% endif %} -{% endfor %} -{% endfor %} diff --git a/provisioner/roles/manage_ec2_instances/templates/skylight_docs_userdata.j2 b/provisioner/roles/manage_ec2_instances/templates/skylight_docs_userdata.j2 deleted file mode 100755 index 43680c369..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/skylight_docs_userdata.j2 +++ /dev/null @@ -1,6 +0,0 @@ -#cloud-config -hostname: "docs" -fqdn: "docs.{{ dns_domain_name }}" -manage_etc_hosts: true -chpasswd: { expire: False } -ssh_pwauth: True diff --git a/provisioner/roles/manage_ec2_instances/templates/skylight_windows_userdata.j2 b/provisioner/roles/manage_ec2_instances/templates/skylight_windows_userdata.j2 deleted file mode 100755 index 41f4dce2b..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/skylight_windows_userdata.j2 +++ /dev/null @@ -1,24 +0,0 @@ - -# Disable .Net Optimization Service -Get-ScheduledTask *ngen* | Disable-ScheduledTask - -# Disable Windows Auto Updates -# https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/troubleshooting-windows-instances.html#high-cpu-issue -reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v AUOptions /t REG_DWORD /d 1 /f -net stop wuauserv -net start wuauserv - -# Remove policies stopping us from enabling WinRM -reg delete "HKLM\SOFTWARE\Policies\Microsoft\Windows\WinRM\Service" /v AllowBasic /f -reg delete "HKLM\SOFTWARE\Policies\Microsoft\Windows\WinRM\Service" /v AllowUnencryptedTraffic /f -reg delete "HKLM\SOFTWARE\Policies\Microsoft\Windows\WinRM\Service" /v DisableRunAs /f - -# Disable Windows Defender Monitoring -Set-MpPreference -DisableRealtimeMonitoring $true - -# Enable WinRM -Invoke-WebRequest -Uri https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1 -OutFile C:\ConfigureRemotingForAnsible.ps1 -C:\ConfigureRemotingForAnsible.ps1 -ForceNewSSLCert -EnableCredSSP - -Rename-Computer -NewName {{ vm_name }} -Force -Restart - diff --git a/provisioner/roles/manage_ec2_instances/templates/windows_userdata.txt.j2 b/provisioner/roles/manage_ec2_instances/templates/windows_userdata.txt.j2 index 8583eadfd..839bb0d01 100644 --- a/provisioner/roles/manage_ec2_instances/templates/windows_userdata.txt.j2 +++ b/provisioner/roles/manage_ec2_instances/templates/windows_userdata.txt.j2 @@ -4,7 +4,14 @@ Get-ScheduledTask *ngen* | Disable-ScheduledTask # Disable Windows Auto Updates # https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/troubleshooting-windows-instances.html#high-cpu-issue -reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v AUOptions /t REG_DWORD /d 1 /f net stop wuauserv net start wuauserv +reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v AUOptions /t REG_DWORD /d 1 /f +net stop wuauserv +net start wuauserv + +# Remove policies stopping us from enabling WinRM +reg delete "HKLM\SOFTWARE\Policies\Microsoft\Windows\WinRM\Service" /v AllowBasic /f +reg delete "HKLM\SOFTWARE\Policies\Microsoft\Windows\WinRM\Service" /v AllowUnencryptedTraffic /f +reg delete "HKLM\SOFTWARE\Policies\Microsoft\Windows\WinRM\Service" /v DisableRunAs /f # Disable Windows Defender Monitoring Set-MpPreference -DisableRealtimeMonitoring $true @@ -13,6 +20,8 @@ Set-MpPreference -DisableRealtimeMonitoring $true Invoke-WebRequest -Uri https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1 -OutFile C:\ConfigureRemotingForAnsible.ps1 C:\ConfigureRemotingForAnsible.ps1 -ForceNewSSLCert -EnableCredSSP +Rename-Computer -NewName {{ vm_name }} -Force -Restart + # Set Administrator Password $admin = [adsi]("WinNT://./administrator, user") $admin.PSBase.Invoke("SetPassword", "{{ windows_password }}") diff --git a/provisioner/security.yml b/provisioner/security.yml index c7fa43d87..e62c88f3c 100644 --- a/provisioner/security.yml +++ b/provisioner/security.yml @@ -118,7 +118,7 @@ name: "webservers_attack_simulation" - name: SETUP WINDOWS WORKSTATION - hosts: windows + hosts: security_windows roles: - role: windows_ws_setup diff --git a/provisioner/windows.yml b/provisioner/windows.yml index 5e5d06d49..8d0c719d9 100644 --- a/provisioner/windows.yml +++ b/provisioner/windows.yml @@ -3,6 +3,8 @@ hosts: gitlab become: true gather_facts: true + vars: + username: "ec2-user" tags: - gitlab tasks: From a173cf3738bb946ac835ecd6383b03b1bfd706d6 Mon Sep 17 00:00:00 2001 From: ipvsean Date: Tue, 28 Apr 2020 19:29:33 -0400 Subject: [PATCH 04/18] more dynamic inventory enchancements also tagging https://github.com/ansible/ansible/issues/69227 in here --- provisioner/ansible.cfg | 1 + provisioner/group_vars/qradar.yml | 3 - provisioner/group_vars/security_windows.yml | 7 -- provisioner/group_vars/splunk.yml | 3 - provisioner/group_vars/windows.yml | 12 --- .../templates/inventory/networking.j2 | 75 ++++++++++++++++++ .../templates/inventory/security.j2 | 36 +++++++++ provisioner/roles/cp_setup/tasks/main.yml | 6 +- .../tasks/instances/instances_f5.yml | 3 + .../tasks/instances/instances_networking.yml | 4 + .../tasks/instances/instances_rhel.yml | 3 + .../tasks/instances/instances_windows.yml | 2 + .../tasks/inventory/dynamic.yml | 8 -- .../security_includes/security_ec2_tags.yml | 6 ++ .../manage_ec2_instances/templates/aws_ec2.j2 | 3 + .../templates/instructor-devops.j2 | 0 .../templates/instructor-f5.j2 | 0 .../templates/instructor-networking.j2 | 0 .../templates/instructor-rhel.j2 | 0 .../templates/instructor-rhel_90.j2 | 1 + .../templates/instructor-security.j2 | 33 ++++++++ .../templates/instructor-windows.j2 | 0 .../templates/instructor_inv.j2 | 4 +- .../instructor_inventory_security.j2 | 47 ------------ .../templates/instructor_inventory_windows.j2 | 76 ------------------- .../templates/security_instances.txt.j2 | 57 -------------- .../security_eth1/templates/ifcfg-eth1.j2 | 2 +- ...hostroutes_from_attacker_to_checkpoint.yml | 4 +- ...dd_hostroutes_from_snort_to_checkpoint.yml | 4 +- provisioner/security.yml | 6 ++ 30 files changed, 184 insertions(+), 222 deletions(-) delete mode 100644 provisioner/group_vars/qradar.yml delete mode 100644 provisioner/group_vars/security_windows.yml delete mode 100644 provisioner/group_vars/splunk.yml delete mode 100644 provisioner/group_vars/windows.yml create mode 100644 provisioner/roles/control_node/templates/inventory/networking.j2 create mode 100644 provisioner/roles/control_node/templates/inventory/security.j2 create mode 100644 provisioner/roles/manage_ec2_instances/templates/instructor-devops.j2 create mode 100644 provisioner/roles/manage_ec2_instances/templates/instructor-f5.j2 create mode 100644 provisioner/roles/manage_ec2_instances/templates/instructor-networking.j2 create mode 100644 provisioner/roles/manage_ec2_instances/templates/instructor-rhel.j2 create mode 100644 provisioner/roles/manage_ec2_instances/templates/instructor-rhel_90.j2 create mode 100644 provisioner/roles/manage_ec2_instances/templates/instructor-security.j2 create mode 100644 provisioner/roles/manage_ec2_instances/templates/instructor-windows.j2 delete mode 100644 provisioner/roles/manage_ec2_instances/templates/instructor_inventory_security.j2 delete mode 100755 provisioner/roles/manage_ec2_instances/templates/instructor_inventory_windows.j2 delete mode 100644 provisioner/roles/manage_ec2_instances/templates/security_instances.txt.j2 diff --git a/provisioner/ansible.cfg b/provisioner/ansible.cfg index c6e525166..3fa51be1b 100644 --- a/provisioner/ansible.cfg +++ b/provisioner/ansible.cfg @@ -8,6 +8,7 @@ # finds first [defaults] +localhost_warning: false interpreter_python = auto_silent stdout_callback = yaml inventory = hosts diff --git a/provisioner/group_vars/qradar.yml b/provisioner/group_vars/qradar.yml deleted file mode 100644 index 37decbfc0..000000000 --- a/provisioner/group_vars/qradar.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -# group_vars for security automation workshop - contact Roland Wolters -ansible_python_interpreter: "python_interpreter" diff --git a/provisioner/group_vars/security_windows.yml b/provisioner/group_vars/security_windows.yml deleted file mode 100644 index 830d31033..000000000 --- a/provisioner/group_vars/security_windows.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -# group_vars for security automation workshop - contact Roland Wolters -ansible_user: Administrator -ansible_password: "{{ windows_password }}" -ansible_port: 5986 -ansible_connection: winrm -ansible_winrm_server_cert_validation: ignore diff --git a/provisioner/group_vars/splunk.yml b/provisioner/group_vars/splunk.yml deleted file mode 100644 index a8b6bdf67..000000000 --- a/provisioner/group_vars/splunk.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -# group_vars for security automation workshop - contact Roland Wolters -ansible_python_interpreter: "{{ec2_info['splunk_enterprise']['python_interpreter']}}" diff --git a/provisioner/group_vars/windows.yml b/provisioner/group_vars/windows.yml deleted file mode 100644 index b42bb40a3..000000000 --- a/provisioner/group_vars/windows.yml +++ /dev/null @@ -1,12 +0,0 @@ ---- -# group_vars for security automation workshop - contact Colin McNaughton -ansible_user: Administrator -ansible_port: 5986 -ansible_winrm_server_cert_validation: ignore -ansible_password: "{{ windows_password }}" -ansible_become_password: "{{ admin_password }}" -ansible_connection: "winrm" -ansible_winrm_transport: "ntlm" -ansible_winrm_server_cert_validation: "ignore" -ansible_winrm_operation_timeout_sec: 120 -ansible_winrm_read_timeout_sec: 150 diff --git a/provisioner/roles/control_node/templates/inventory/networking.j2 b/provisioner/roles/control_node/templates/inventory/networking.j2 new file mode 100644 index 000000000..eabb8f88d --- /dev/null +++ b/provisioner/roles/control_node/templates/inventory/networking.j2 @@ -0,0 +1,75 @@ +{% set rtr1 = ec2_name_prefix + '-' + username + '-rtr1' %} +{% set rtr2 = ec2_name_prefix + '-' + username + '-rtr2' %} +{% set rtr3 = ec2_name_prefix + '-' + username + '-rtr3' %} +{% set rtr4 = ec2_name_prefix + '-' + username + '-rtr4' %} + + +[all:vars] +ansible_ssh_private_key_file=/home/{{username}}/.ssh/aws-private.pem + +[routers:children] +{% if network_type == "multivendor" or network_type == "cisco" %} +cisco +{% endif %} +{% if network_type == "multivendor" or network_type == "juniper" %} +juniper +{% endif %} +{% if network_type == "multivendor" or network_type == "arista" %} +arista +{% endif %} + +{% if network_type == "multivendor" or network_type == "cisco" %} +[cisco] +{% endif %} +{% if network_type == "arista" %} +[arista] +{% endif %} +{% if network_type == "juniper" %} +[juniper] +{% endif %} +{{ hostvars[rtr1].short_name }} ansible_host={{ hostvars[rtr1].ansible_host }} private_ip={{ hostvars[rtr1].private_ip }} +{% if network_type == "multivendor" %} +[arista] +{% endif %} +{{ hostvars[rtr2].short_name }} ansible_host={{ hostvars[rtr2].ansible_host }} private_ip={{ hostvars[rtr2].private_ip }} +{{ hostvars[rtr4].short_name }} ansible_host={{ hostvars[rtr4].ansible_host }} private_ip={{ hostvars[rtr4].private_ip }} +{% if network_type == "multivendor" %} +[juniper] +{% endif %} +{{ hostvars[rtr3].short_name }} ansible_host={{ hostvars[rtr3].ansible_host }} private_ip={{ hostvars[rtr3].private_ip }} + + +{% if network_type == "multivendor" or network_type == "cisco" %} +[cisco:vars] +ansible_user=ec2-user +ansible_network_os=ios +ansible_connection=network_cli +{% endif %} + +{% if network_type == "multivendor" or network_type == "juniper" %} +[juniper:vars] +ansible_user=ec2-user +ansible_network_os=junos +ansible_connection=netconf +{% endif %} + +{% if network_type == "multivendor" or network_type == "arista" %} +[arista:vars] +ansible_user=ec2-user +ansible_network_os=eos +ansible_connection=network_cli +ansible_become=true +ansible_become_method=enable +{% endif %} + +[dc1] +rtr1 +rtr3 + +[dc2] +rtr2 +rtr4 + +[control] +{% set control_node = ec2_name_prefix + '-' + username + '-ansible' %} +{{ hostvars[control_node].short_name }} ansible_host={{ hostvars[control_node].ansible_host }} diff --git a/provisioner/roles/control_node/templates/inventory/security.j2 b/provisioner/roles/control_node/templates/inventory/security.j2 new file mode 100644 index 000000000..86d46c045 --- /dev/null +++ b/provisioner/roles/control_node/templates/inventory/security.j2 @@ -0,0 +1,36 @@ +[all:vars] +ansible_user={{username}} +ansible_password={{ admin_password }} +{% if ssh_port is defined %} +ansible_port={{ ssh_port }} +{% endif %} + +[control] +{% set control_node = ec2_name_prefix + '-' + username + '-ansible' %} +{{ hostvars[control_node].short_name }} ansible_host={{ hostvars[control_node].ansible_host }} private_ip={{ hostvars[control_node].private_ip }} + +[attack] +{% set attacker = ec2_name_prefix + '-' + username + '-attacker' %} +{{ hostvars[attacker].short_name }} ansible_host={{ hostvars[attacker].ansible_host }} ansible_user={{ hostvars[attacker].ansible_user }} private_ip={{ hostvars[attacker].security_interface_one }} private_ip2={{ hostvars[attacker].security_interface_two }} + +[siem] +{% if security_console == 'splunk' %} +{% set splunk = ec2_name_prefix + '-' + username + '-splunk' %} +{{ hostvars[splunk].short_name }} ansible_host={{ hostvars[splunk].ansible_host }} ansible_user=admin private_ip={{ hostvars[splunk].private_ip }} ansible_httpapi_pass="Ansible1!" ansible_connection=httpapi ansible_httpapi_use_ssl=yes ansible_httpapi_validate_certs=False ansible_network_os=splunk.enterprise_security.splunk +{% endif %} +{% if security_console == 'qradar' %} +{% set qradar = ec2_name_prefix + '-' + username + '-qradar' %} +{{ hostvars[qradar].short_name }} ansible_host={{ hostvars[qradar].ansible_host }} ansible_user=admin private_ip={{ hostvars[qradar].private_ip }} ansible_httpapi_pass="Ansible1!" ansible_connection=httpapi ansible_httpapi_use_ssl=yes ansible_httpapi_validate_certs=False ansible_network_os=ibm.qradar.qradar +{% endif %} + +[ids] +{% set snort = ec2_name_prefix + '-' + username + '-snort' %} +{{ hostvars[snort].short_name }} ansible_host={{ hostvars[snort].ansible_host }} ansible_user={{ hostvars[snort].ansible_user }} private_ip={{ hostvars[snort].security_interface_one }} private_ip2={{ hostvars[snort].security_interface_two }} + +[firewall] +{% set checkpoint = ec2_name_prefix + '-' + username + '-checkpoint_mgmt' %} +checkpoint ansible_host={{ hostvars[checkpoint].ansible_host }} ansible_user=admin ansible_password=admin123 private_ip={{ hostvars[checkpoint].private_ip }} ansible_network_os=checkpoint ansible_connection=httpapi ansible_httpapi_use_ssl=yes ansible_httpapi_validate_certs=no + +[windows] +{% set windows = ec2_name_prefix + '-' + username + '-windows_ws' %} +windows ansible_host={{ hostvars[windows].ansible_host }} ansible_user=Administrator ansible_password={{ windows_password}} ansible_port=5986 ansible_connection=winrm ansible_winrm_server_cert_validation=ignore private_ip={{ hostvars[windows].private_ip }} diff --git a/provisioner/roles/cp_setup/tasks/main.yml b/provisioner/roles/cp_setup/tasks/main.yml index 60b2105f9..8170c2edf 100644 --- a/provisioner/roles/cp_setup/tasks/main.yml +++ b/provisioner/roles/cp_setup/tasks/main.yml @@ -24,7 +24,7 @@ body_format: json body: name: myngfw - ip-address: "{{ hostvars[inventory_hostname|regex_replace('ansible', 'checkpoint_gw')]['private_ip'] }}" + ip-address: "{{ hostvars[inventory_hostname|regex_replace('ansible', 'checkpoint_gw')]['security_interface_one'] }}" one-time-password: admin123 firewall: true version: R80.30 @@ -34,7 +34,7 @@ anti-spoofing-settings: action: prevent name: "eth0" - ip-address: "{{ hostvars[inventory_hostname|regex_replace('ansible', 'checkpoint_gw')]['private_ip'] }}" + ip-address: "{{ hostvars[inventory_hostname|regex_replace('ansible', 'checkpoint_gw')]['security_interface_one'] }}" network-mask: "255.255.0.0" ipv4-mask-length: 16 security-zone: false @@ -45,7 +45,7 @@ anti-spoofing-settings: action: prevent name: "eth1" - ip-address: "{{ hostvars[inventory_hostname|regex_replace('ansible', 'checkpoint_gw')]['private_ip2'] }}" + ip-address: "{{ hostvars[inventory_hostname|regex_replace('ansible', 'checkpoint_gw')]['security_interface_two'] }}" network-mask: "255.255.0.0" ipv4-mask-length: 16 security-zone: false diff --git a/provisioner/roles/manage_ec2_instances/tasks/instances/instances_f5.yml b/provisioner/roles/manage_ec2_instances/tasks/instances/instances_f5.yml index 42bdc3a03..4267f01e7 100644 --- a/provisioner/roles/manage_ec2_instances/tasks/instances/instances_f5.yml +++ b/provisioner/roles/manage_ec2_instances/tasks/instances/instances_f5.yml @@ -25,6 +25,7 @@ short_name: "f5" Workshop_f5: "{{ec2_name_prefix}}-f5" Workshop: "{{ec2_name_prefix}}" + Workshop_type: "{{ workshop_type }}" Index: "{{ item[0] }}" Student: "student{{item.0 + 1}}" AWS_USERNAME: "{{ aws_user }}" @@ -60,6 +61,7 @@ Name: "{{ ec2_name_prefix }}-student{{item.0 + 1}}-node1" Workshop_node1: "{{ec2_name_prefix}}-node1" Workshop: "{{ec2_name_prefix}}" + Workshop_type: "{{ workshop_type }}" Index: "{{ item[0] }}" Student: "student{{item.0 + 1}}" AWS_USERNAME: "{{ aws_user }}" @@ -97,6 +99,7 @@ Name: "{{ ec2_name_prefix }}-student{{item.0 + 1}}-node2" Workshop_node2: "{{ec2_name_prefix}}-node2" Workshop: "{{ec2_name_prefix}}" + Workshop_type: "{{ workshop_type }}" Index: "{{ item[0] }}" Student: "student{{item.0 + 1}}" AWS_USERNAME: "{{ aws_user }}" diff --git a/provisioner/roles/manage_ec2_instances/tasks/instances/instances_networking.yml b/provisioner/roles/manage_ec2_instances/tasks/instances/instances_networking.yml index e06bb816c..d5df7234e 100644 --- a/provisioner/roles/manage_ec2_instances/tasks/instances/instances_networking.yml +++ b/provisioner/roles/manage_ec2_instances/tasks/instances/instances_networking.yml @@ -34,6 +34,7 @@ Name: "{{ ec2_name_prefix }}-student{{item.0 + 1}}-rtr1" Workshop_rtr1: "{{ec2_name_prefix}}-rtr1" Workshop: "{{ec2_name_prefix}}" + Workshop_type: "{{ workshop_type }}" Index: "{{ item[0] }}" Student: "student{{item.0 + 1}}" AWS_USERNAME: "{{ aws_user }}" @@ -76,6 +77,7 @@ Name: "{{ ec2_name_prefix }}-student{{item.0 + 1}}-rtr2" Workshop_rtr2: "{{ec2_name_prefix}}-rtr2" Workshop: "{{ec2_name_prefix}}" + Workshop_type: "{{ workshop_type }}" Index: "{{ item[0] }}" Student: "student{{item.0 + 1}}" AWS_USERNAME: "{{ aws_user }}" @@ -118,6 +120,7 @@ Name: "{{ ec2_name_prefix }}-student{{item.0 + 1}}-rtr3" Workshop_rtr3: "{{ec2_name_prefix}}-rtr3" Workshop: "{{ec2_name_prefix}}" + Workshop_type: "{{ workshop_type }}" Index: "{{ item[0] }}" Student: "student{{item.0 + 1}}" AWS_USERNAME: "{{ aws_user }}" @@ -160,6 +163,7 @@ Name: "{{ ec2_name_prefix }}-student{{item.0 + 1}}-rtr4" Workshop_rtr4: "{{ec2_name_prefix}}-rtr4" Workshop: "{{ec2_name_prefix}}" + Workshop_type: "{{ workshop_type }}" Index: "{{ item[0] }}" Student: "student{{item.0 + 1}}" AWS_USERNAME: "{{ aws_user }}" diff --git a/provisioner/roles/manage_ec2_instances/tasks/instances/instances_rhel.yml b/provisioner/roles/manage_ec2_instances/tasks/instances/instances_rhel.yml index fa80b93a6..8f5b7b7a6 100644 --- a/provisioner/roles/manage_ec2_instances/tasks/instances/instances_rhel.yml +++ b/provisioner/roles/manage_ec2_instances/tasks/instances/instances_rhel.yml @@ -28,6 +28,7 @@ Name: "{{ ec2_name_prefix }}-student{{item.0 + 1}}-node1" Workshop_node1: "{{ec2_name_prefix}}-node1" Workshop: "{{ec2_name_prefix}}" + Workshop_type: "{{ workshop_type }}" Index: "{{ item[0] }}" Student: "student{{item.0 + 1}}" AWS_USERNAME: "{{ aws_user }}" @@ -70,6 +71,7 @@ Name: "{{ ec2_name_prefix }}-student{{item.0 + 1}}-node2" Workshop_node2: "{{ec2_name_prefix}}-node2" Workshop: "{{ec2_name_prefix}}" + Workshop_type: "{{ workshop_type }}" Index: "{{ item[0] }}" Student: "student{{item.0 + 1}}" AWS_USERNAME: "{{ aws_user }}" @@ -112,6 +114,7 @@ Name: "{{ ec2_name_prefix }}-student{{item.0 + 1}}-node3" Workshop_node3: "{{ec2_name_prefix}}-node3" Workshop: "{{ec2_name_prefix}}" + Workshop_type: "{{ workshop_type }}" Index: "{{ item[0] }}" Student: "student{{item.0 + 1}}" AWS_USERNAME: "{{ aws_user }}" diff --git a/provisioner/roles/manage_ec2_instances/tasks/instances/instances_windows.yml b/provisioner/roles/manage_ec2_instances/tasks/instances/instances_windows.yml index e98b2275c..9b8f59d36 100644 --- a/provisioner/roles/manage_ec2_instances/tasks/instances/instances_windows.yml +++ b/provisioner/roles/manage_ec2_instances/tasks/instances/instances_windows.yml @@ -82,6 +82,7 @@ Students: "{{student_total}}" short_name: "instance1" launch_time: "{{item.1.launch_time}}" + username: "{{ ec2_info['windows_ws']['username'] }}" with_indexed_items: - "{{ instance1_output.instances }}" when: instance1_output.instance_ids is not none @@ -105,6 +106,7 @@ Students: "{{student_total}}" short_name: "instance2" launch_time: "{{item.1.launch_time}}" + username: "{{ ec2_info['windows_ws']['username'] }}" with_indexed_items: - "{{ instance2_output.instances }}" when: diff --git a/provisioner/roles/manage_ec2_instances/tasks/inventory/dynamic.yml b/provisioner/roles/manage_ec2_instances/tasks/inventory/dynamic.yml index 9020f2c1c..4d67e29c0 100644 --- a/provisioner/roles/manage_ec2_instances/tasks/inventory/dynamic.yml +++ b/provisioner/roles/manage_ec2_instances/tasks/inventory/dynamic.yml @@ -11,14 +11,6 @@ - name: refresh AWS inventory information meta: refresh_inventory -- group_by: - key: security_connection_check - parents: - - attack - - snort - - qradar - when: workshop_type == "security" - - name: display all groups from dynamic inventory debug: msg: "{{ groups }}" diff --git a/provisioner/roles/manage_ec2_instances/tasks/security_includes/security_ec2_tags.yml b/provisioner/roles/manage_ec2_instances/tasks/security_includes/security_ec2_tags.yml index 0296664f9..c2a1b0fcb 100644 --- a/provisioner/roles/manage_ec2_instances/tasks/security_includes/security_ec2_tags.yml +++ b/provisioner/roles/manage_ec2_instances/tasks/security_includes/security_ec2_tags.yml @@ -8,6 +8,7 @@ Name: "{{ ec2_name_prefix }}-student{{item[0] + 1}}-splunk" Workshop_splunk: "{{ec2_name_prefix}}-splunk" Workshop: "{{ec2_name_prefix}}" + Workshop_type: "{{ workshop_type }}" Index: "{{ item[0] }}" Student: "student{{item[0] + 1}}" AWS_USERNAME: "{{ aws_user }}" @@ -34,6 +35,7 @@ Name: "{{ ec2_name_prefix }}-student{{item[0] + 1}}-qradar" Workshop_qradar: "{{ec2_name_prefix}}-qradar" Workshop: "{{ec2_name_prefix}}" + Workshop_type: "{{ workshop_type }}" Index: "{{ item[0] }}" Student: "student{{item[0] + 1}}" AWS_USERNAME: "{{ aws_user }}" @@ -67,6 +69,7 @@ Name: "{{ ec2_name_prefix }}-student{{item[0] + 1}}-snort" Workshop_snort: "{{ec2_name_prefix}}-snort" Workshop: "{{ec2_name_prefix}}" + Workshop_type: "{{ workshop_type }}" Index: "{{ item[0] }}" Student: "student{{item[0] + 1}}" AWS_USERNAME: "{{ aws_user }}" @@ -92,6 +95,7 @@ Name: "{{ ec2_name_prefix }}-student{{item[0] + 1}}-attacker" Workshop_attacker: "{{ec2_name_prefix}}-attacker" Workshop: "{{ec2_name_prefix}}" + Workshop_type: "{{ workshop_type }}" Index: "{{ item[0] }}" Student: "student{{item[0] + 1}}" AWS_USERNAME: "{{ aws_user }}" @@ -118,6 +122,7 @@ Name: "{{ ec2_name_prefix }}-student{{item[0] + 1}}-checkpoint_mgmt" Workshop_checkpoint_mgmt: "{{ec2_name_prefix}}-checkpoint" Workshop: "{{ec2_name_prefix}}" + Workshop_type: "{{ workshop_type }}" Index: "{{ item[0] }}" Student: "student{{item[0] + 1}}" AWS_USERNAME: "{{ aws_user }}" @@ -145,6 +150,7 @@ Name: "{{ ec2_name_prefix }}-student{{item[0] + 1}}-windows_ws" Workshop_windows: "{{ec2_name_prefix}}-windows" Workshop: "{{ec2_name_prefix}}" + Workshop_type: "{{ workshop_type }}" Index: "{{ item[0] }}" Student: "student{{item[0] + 1}}" AWS_USERNAME: "{{ aws_user }}" diff --git a/provisioner/roles/manage_ec2_instances/templates/aws_ec2.j2 b/provisioner/roles/manage_ec2_instances/templates/aws_ec2.j2 index ba5a25e08..3984aa7b6 100644 --- a/provisioner/roles/manage_ec2_instances/templates/aws_ec2.j2 +++ b/provisioner/roles/manage_ec2_instances/templates/aws_ec2.j2 @@ -14,6 +14,7 @@ groups: core: "'core' in tags.group" access: "'access' in tags.group" # f5 + f5: "'f5' in tags.short_name" # windows windows: "'instance' in tags.short_name" gitlab: "'gitlab' in tags.short_name" @@ -40,3 +41,5 @@ compose: ansible_ssh_private_key_file: '"{{ playbook_dir }}/{{ec2_name_prefix}}/{{ec2_name_prefix}}-private.pem"' private_ip: private_ip_address ansible_network_os: tags.ansible_network_os + security_interface_one: network_interfaces|map(attribute='private_ip_address')|list|ipaddr('172.16.0.0/16')|first + security_interface_two: network_interfaces|map(attribute='private_ip_address')|list|ipaddr('172.17.0.0/16')|first diff --git a/provisioner/roles/manage_ec2_instances/templates/instructor-devops.j2 b/provisioner/roles/manage_ec2_instances/templates/instructor-devops.j2 new file mode 100644 index 000000000..e69de29bb diff --git a/provisioner/roles/manage_ec2_instances/templates/instructor-f5.j2 b/provisioner/roles/manage_ec2_instances/templates/instructor-f5.j2 new file mode 100644 index 000000000..e69de29bb diff --git a/provisioner/roles/manage_ec2_instances/templates/instructor-networking.j2 b/provisioner/roles/manage_ec2_instances/templates/instructor-networking.j2 new file mode 100644 index 000000000..e69de29bb diff --git a/provisioner/roles/manage_ec2_instances/templates/instructor-rhel.j2 b/provisioner/roles/manage_ec2_instances/templates/instructor-rhel.j2 new file mode 100644 index 000000000..e69de29bb diff --git a/provisioner/roles/manage_ec2_instances/templates/instructor-rhel_90.j2 b/provisioner/roles/manage_ec2_instances/templates/instructor-rhel_90.j2 new file mode 100644 index 000000000..80fba6e5c --- /dev/null +++ b/provisioner/roles/manage_ec2_instances/templates/instructor-rhel_90.j2 @@ -0,0 +1 @@ +{% include 'instructor-rhel.j2' %} diff --git a/provisioner/roles/manage_ec2_instances/templates/instructor-security.j2 b/provisioner/roles/manage_ec2_instances/templates/instructor-security.j2 new file mode 100644 index 000000000..7561eba6b --- /dev/null +++ b/provisioner/roles/manage_ec2_instances/templates/instructor-security.j2 @@ -0,0 +1,33 @@ + +[attack] +{% for vm in groups.attack %} +{{ hostvars[vm].username }}-{{ hostvars[vm].short_name }} ansible_host={{ hostvars[vm].ansible_host }} ansible_user={{ hostvars[vm].ansible_user }} private_ip={{ hostvars[vm].security_interface_one }} private_ip2={{ hostvars[vm].security_interface_two }} +{% endfor %} + +[siem] +{% if security_console == 'splunk' %} +{% for vm in groups.splunk %} +{{ hostvars[vm].username }}-{{ hostvars[vm].short_name }} ansible_host={{ hostvars[vm].ansible_host }} ansible_user={{ hostvars[vm].ansible_user }} private_ip={{ hostvars[vm].private_ip }} ansible_httpapi_pass="Ansible1!" ansible_connection=httpapi ansible_httpapi_use_ssl=yes ansible_httpapi_validate_certs=False ansible_network_os=splunk.enterprise_security.splunk +{% endfor %} +{% endif %} + +{% if security_console == 'qradar' %} +{% for vm in groups.qradar %} +{{ hostvars[vm].username }}-{{ hostvars[vm].short_name }} ansible_host={{ hostvars[vm].ansible_host }} ansible_user={{ hostvars[vm].ansible_user }} private_ip={{ hostvars[vm].private_ip }} ansible_httpapi_pass="Ansible1!" ansible_connection=httpapi ansible_httpapi_use_ssl=yes ansible_httpapi_validate_certs=False ansible_network_os=ibm.qradar.qradar +{% endfor %} +{% endif %} + +[ids] +{% for vm in groups.snort %} +{{ hostvars[vm].username }}-{{ hostvars[vm].short_name }} ansible_host={{ hostvars[vm].ansible_host }} ansible_user={{ hostvars[vm].ansible_user }} private_ip={{ hostvars[vm].security_interface_one }} private_ip2={{ hostvars[vm].security_interface_two }} +{% endfor %} + +[firewall] +{% for vm in groups.checkpoint_mgmt %} +{{ hostvars[vm].username }}-{{ hostvars[vm].short_name }} ansible_host={{ hostvars[vm].ansible_host }} ansible_user={{ hostvars[vm].ansible_user }} ansible_password=admin123 private_ip={{ hostvars[vm].private_ip }} ansible_network_os=checkpoint ansible_connection=httpapi ansible_httpapi_use_ssl=yes ansible_httpapi_validate_certs=no +{% endfor %} + +[windows] +{% for vm in groups.security_windows %} +{{ hostvars[vm].username }}-{{ hostvars[vm].short_name }} ansible_host={{ hostvars[vm].ansible_host }} ansible_user={{ hostvars[vm].ansible_user }} ansible_password={{ windows_password}} ansible_port=5986 ansible_connection=winrm ansible_winrm_server_cert_validation=ignore private_ip={{ hostvars[vm].private_ip }} +{% endfor %} diff --git a/provisioner/roles/manage_ec2_instances/templates/instructor-windows.j2 b/provisioner/roles/manage_ec2_instances/templates/instructor-windows.j2 new file mode 100644 index 000000000..e69de29bb diff --git a/provisioner/roles/manage_ec2_instances/templates/instructor_inv.j2 b/provisioner/roles/manage_ec2_instances/templates/instructor_inv.j2 index 14e6edb3d..2ee290bec 100644 --- a/provisioner/roles/manage_ec2_instances/templates/instructor_inv.j2 +++ b/provisioner/roles/manage_ec2_instances/templates/instructor_inv.j2 @@ -1,15 +1,17 @@ [all:vars] +ansible_password={{ admin_password }} ansible_ssh_private_key_file={{ playbook_dir }}/{{ ec2_name_prefix }}/{{ ec2_name_prefix }}-private.pem {% if ssh_port is defined %} ansible_port={{ ssh_port }} {% endif %} +{% include 'instructor-' + workshop_type + '.j2' %} {% for number in range(1,student_total + 1) %} [student{{ number }}] {% for vm in groups['student' + number|string] %} -{{ hostvars[vm].username }}-{{ hostvars[vm].short_name }} ansible_host={{ hostvars[vm].ansible_host }} ansible_user={{ hostvars[vm].ansible_user }} +{{ hostvars[vm].username }}-{{ hostvars[vm].short_name }} {% endfor %} {% endfor %} diff --git a/provisioner/roles/manage_ec2_instances/templates/instructor_inventory_security.j2 b/provisioner/roles/manage_ec2_instances/templates/instructor_inventory_security.j2 deleted file mode 100644 index 846aeccb2..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/instructor_inventory_security.j2 +++ /dev/null @@ -1,47 +0,0 @@ -[all:vars] -{% if ssh_port is defined %} -ansible_port={{ ssh_port }} -{% endif %} - -{% for number in range(1,student_total + 1) %} -[student{{ number }}] -{% for host in ansible_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{host.tags.Student}}-ansible ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} -{% endif %} -{% endfor %} -{% if security_console == 'splunk' %} -{% for host in splunk_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{host.tags.Student}}-splunk ansible_host={{ host.public_ip_address }} ansible_user=admin -{% endif %} -{% endfor %} -{% endif %} -{% if security_console == 'qradar' %} -{% for host in qradar_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{host.tags.Student}}-qradar ansible_host={{ host.public_ip_address }} ansible_user=admin ansible_httpapi_pass="Ansible1!" ansible_connection=httpapi ansible_httpapi_use_ssl=yes ansible_httpapi_validate_certs=False ansible_network_os=ibm.qradar.qradar -{% endif %} -{% endfor %} -{% endif %} -{% for host in snort_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{host.tags.Student}}-snort ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} private_ip={{ hostvars[host.tags.Name]['private_ip'] }} private_ip2={{ hostvars[host.tags.Name]['private_ip2'] }} -{% endif %} -{% endfor %} -{% for host in attacker_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{host.tags.Student}}-attacker ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} private_ip={{ hostvars[host.tags.Name]['private_ip'] }} private_ip2={{ hostvars[host.tags.Name]['private_ip2'] }} -{% endif %} -{% endfor %} -{% for host in checkpoint_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{host.tags.Student}}-checkpoint ansible_host={{ host.public_ip_address }} ansible_user={{ host.tags.username }} ansible_password=admin123 ansible_network_os=checkpoint ansible_connection=httpapi ansible_httpapi_use_ssl=yes ansible_httpapi_validate_certs=no -{% endif %} -{% endfor %} -{% for host in windows_node_facts.instances %} -{% if 'student' ~ number == host.tags.Student %} -{{host.tags.Student}}-windows-ws ansible_host={{ host.public_ip_address }} ansible_user=Administrator ansible_password={{ windows_password }} ansible_port=5986 ansible_connection=winrm ansible_winrm_server_cert_validation=ignore -{% endif %} -{% endfor %} -{% endfor %} diff --git a/provisioner/roles/manage_ec2_instances/templates/instructor_inventory_windows.j2 b/provisioner/roles/manage_ec2_instances/templates/instructor_inventory_windows.j2 deleted file mode 100755 index 898c56321..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/instructor_inventory_windows.j2 +++ /dev/null @@ -1,76 +0,0 @@ -[all:vars] -{% if ssh_port is defined %} -ansible_port={{ ssh_port }} -{% endif %} - -[windows:vars] -ansible_connection=winrm -ansible_winrm_transport=credssp -ansible_winrm_server_cert_validation=ignore -ansible_port=5986 - -[windows_workstations:vars] -ansible_connection=winrm -ansible_winrm_transport=credssp -ansible_winrm_server_cert_validation=ignore -ansible_port=5986 - -{# [windows_domain_controllers:vars] -ansible_connection=winrm -ansible_winrm_transport=credssp -ansible_winrm_server_cert_validation=ignore -ansible_port=5986 #} - -[control_nodes:vars] -ansible_port=22 -ansible_user=ec2-user -ansible_ssh_private_key_file="{{ playbook_dir }}/{{ ec2_name_prefix }}/{{ ec2_name_prefix }}-private.pem" - -[gitlab:vars] -ansible_port=22 -ansible_user={{ ec2_info['skylight_rhel8_gitlab']['username'] }} -ansible_ssh_private_key_file="{{ playbook_dir }}/{{ ec2_name_prefix }}/{{ ec2_name_prefix }}-private.pem" - -{# [docs:vars] -ansible_port=22 -ansible_user={{ root_user }} -ansible_ssh_private_key_file="{{ playbook_dir }}/{{ ec2_name_prefix }}/{{ ec2_name_prefix }}-private.pem" #} - - -[instructor] -{% for host in hostvars %} -{% if "windc" in host %} -{{ host|replace(ec2_name_prefix + "-", "") }} ansible_host={{ hostvars[host].ansible_host }} ansible_user={{ hostvars[host].ansible_user }} ansible_password="{{ hostvars[host].ansible_password }}" private_ip={{ hostvars[host].private_ip }} -{% elif "gitlab" in host %} -{{ host|replace(ec2_name_prefix + "-", "") }} ansible_host={{ hostvars[host].ansible_host }} -{% elif "docs" in host %} -{{ host|replace(ec2_name_prefix + "-", "") }} ansible_host={{ hostvars[host].ansible_host }} -{% endif %} -{% endfor %} - -{% for user in range(1,student_total + 1) %} -[student{{ user }}] -{% for host in hostvars %} -{% if "student" + user|string + "-" in host -%} -{% if "windc" in host -%} -{% elif "gitlab" in host -%} -{% elif "docs" in host -%} -{% elif "ansible" in host %} -{{ host|replace(ec2_name_prefix + "-", "") }} ansible_host={{ hostvars[host].ansible_host }} -{% else %} -{{ host|replace(ec2_name_prefix + "-", "") }} ansible_host={{ hostvars[host].ansible_host }} ansible_user={{ hostvars[host].ansible_user }} ansible_password="{{ hostvars[host].ansible_password }}" student="{{ hostvars[host].student }}" -{% endif %} -{% endif %} -{% endfor %} -{% endfor %} - -{% for group in groups %} -[{{group}}] -{% for entry in groups[group] %} -{% for host in hostvars %} -{% if entry == host %} -{{ host|replace(ec2_name_prefix + "-", "") }} -{% endif %} -{% endfor %} -{% endfor %} -{% endfor %} diff --git a/provisioner/roles/manage_ec2_instances/templates/security_instances.txt.j2 b/provisioner/roles/manage_ec2_instances/templates/security_instances.txt.j2 deleted file mode 100644 index 673e179b5..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/security_instances.txt.j2 +++ /dev/null @@ -1,57 +0,0 @@ -[all:vars] -ansible_user=student{{ item }} -ansible_ssh_pass={{ admin_password }} -{% if ssh_port is defined %} -ansible_port={{ ssh_port }} -{% endif %} - -[control] -{% for vm in ansible_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -ansible ansible_host={{ vm.public_ip_address }} private_ip={{ hostvars[vm.tags.Name]['private_ip'] }} -{% endif %} -{% endfor %} - -[attack] -{% for vm in attacker_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -attacker ansible_host={{ vm.public_ip_address }} ansible_user={{ vm.tags.username }} private_ip={{ hostvars[vm.tags.Name]['private_ip'] }} private_ip2={{ hostvars[vm.tags.Name]['private_ip2'] }} -{% endif %} -{% endfor %} - -[siem] -{% if security_console == 'splunk' %} -{% for vm in splunk_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -splunk ansible_host={{ vm.public_ip_address }} ansible_user=admin private_ip={{ hostvars[vm.tags.Name]['private_ip'] }} ansible_httpapi_pass="Ansible1!" ansible_connection=httpapi ansible_httpapi_use_ssl=yes ansible_httpapi_validate_certs=False ansible_network_os=splunk.enterprise_security.splunk -{% endif %} -{% endfor %} -{% endif %} -{% if security_console == 'qradar' %} -{% for vm in qradar_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -qradar ansible_host={{ vm.public_ip_address }} ansible_user=admin private_ip={{ hostvars[vm.tags.Name]['private_ip'] }} ansible_httpapi_pass="Ansible1!" ansible_connection=httpapi ansible_httpapi_use_ssl=yes ansible_httpapi_validate_certs=False ansible_network_os=ibm.qradar.qradar -{% endif %} -{% endfor %} -{% endif %} - -[ids] -{% for vm in snort_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -snort ansible_host={{ vm.public_ip_address }} ansible_user={{ vm.tags.username }} private_ip={{ hostvars[vm.tags.Name]['private_ip'] }} private_ip2={{ hostvars[vm.tags.Name]['private_ip2'] }} -{% endif %} -{% endfor %} - -[firewall] -{% for vm in checkpoint_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -checkpoint ansible_host={{ vm.public_ip_address }} ansible_user={{ vm.tags.username }} ansible_password=admin123 private_ip={{ hostvars[vm.tags.Name]['private_ip'] }} ansible_network_os=checkpoint ansible_connection=httpapi ansible_httpapi_use_ssl=yes ansible_httpapi_validate_certs=no -{% endif %} -{% endfor %} - -[windows] -{% for vm in windows_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -windows-ws ansible_host={{ vm.public_ip_address }} ansible_user={{ vm.tags.username }} ansible_pass={{ windows_password}} ansible_port=5986 ansible_connection=winrm ansible_winrm_server_cert_validation=ignore private_ip={{ hostvars[vm.tags.Name]['private_ip'] }} -{% endif %} -{% endfor %} diff --git a/provisioner/roles/security_eth1/templates/ifcfg-eth1.j2 b/provisioner/roles/security_eth1/templates/ifcfg-eth1.j2 index 067d14d3c..d747838dd 100644 --- a/provisioner/roles/security_eth1/templates/ifcfg-eth1.j2 +++ b/provisioner/roles/security_eth1/templates/ifcfg-eth1.j2 @@ -7,5 +7,5 @@ ONBOOT=yes TYPE=Ethernet USERCTL=no PREFIX=16 -IPADDR={{ hostvars[inventory_hostname]['private_ip2'] }} +IPADDR={{ hostvars[inventory_hostname]['security_interface_two'] }} DEFROUTE=no diff --git a/provisioner/roles/security_hostroutes/tasks/add_hostroutes_from_attacker_to_checkpoint.yml b/provisioner/roles/security_hostroutes/tasks/add_hostroutes_from_attacker_to_checkpoint.yml index 2ee988638..05970f98d 100644 --- a/provisioner/roles/security_hostroutes/tasks/add_hostroutes_from_attacker_to_checkpoint.yml +++ b/provisioner/roles/security_hostroutes/tasks/add_hostroutes_from_attacker_to_checkpoint.yml @@ -2,7 +2,7 @@ - name: "add gw route on attacker node {{ inventory_hostname }}" lineinfile: path: /etc/sysconfig/network-scripts/route-eth1 - line: "{{ hostvars[inventory_hostname|regex_replace('attacker', 'checkpoint_gw')]['private_ip2'] }} dev eth1" + line: "{{ hostvars[inventory_hostname|regex_replace('attacker', 'checkpoint_gw')]['security_interface_two'] }} dev eth1" create: true notify: - restart-network @@ -10,7 +10,7 @@ - name: "add snort route on attacker node {{ inventory_hostname }}" lineinfile: path: /etc/sysconfig/network-scripts/route-eth1 - line: "{{ hostvars[inventory_hostname|regex_replace('attacker', 'snort')]['private_ip2'] }} via {{ hostvars[inventory_hostname|regex_replace('attacker', 'checkpoint_gw')]['private_ip2'] }} dev eth1 metric 5" + line: "{{ hostvars[inventory_hostname|regex_replace('attacker', 'snort')]['security_interface_two'] }} via {{ hostvars[inventory_hostname|regex_replace('attacker', 'checkpoint_gw')]['security_interface_two'] }} dev eth1 metric 5" create: true notify: - restart-network diff --git a/provisioner/roles/security_hostroutes/tasks/add_hostroutes_from_snort_to_checkpoint.yml b/provisioner/roles/security_hostroutes/tasks/add_hostroutes_from_snort_to_checkpoint.yml index a86f50e0b..5cbffa789 100644 --- a/provisioner/roles/security_hostroutes/tasks/add_hostroutes_from_snort_to_checkpoint.yml +++ b/provisioner/roles/security_hostroutes/tasks/add_hostroutes_from_snort_to_checkpoint.yml @@ -2,7 +2,7 @@ - name: "add gw route on snort node {{ inventory_hostname }}" lineinfile: path: /etc/sysconfig/network-scripts/route-eth1 - line: "{{ hostvars[inventory_hostname|regex_replace('snort', 'checkpoint_gw')]['private_ip2'] }} dev eth1" + line: "{{ hostvars[inventory_hostname|regex_replace('snort', 'checkpoint_gw')]['security_interface_two'] }} dev eth1" create: true notify: - restart-network @@ -10,7 +10,7 @@ - name: "add attacker route on snort node {{ inventory_hostname }}" lineinfile: path: /etc/sysconfig/network-scripts/route-eth1 - line: "{{ hostvars[inventory_hostname|regex_replace('snort', 'attacker')]['private_ip2'] }} via {{ hostvars[inventory_hostname|regex_replace('snort', 'checkpoint_gw')]['private_ip2'] }} dev eth1 metric 5" + line: "{{ hostvars[inventory_hostname|regex_replace('snort', 'attacker')]['security_interface_two'] }} via {{ hostvars[inventory_hostname|regex_replace('snort', 'checkpoint_gw')]['security_interface_two'] }} dev eth1 metric 5" create: true notify: - restart-network diff --git a/provisioner/security.yml b/provisioner/security.yml index e62c88f3c..ec668d0e6 100644 --- a/provisioner/security.yml +++ b/provisioner/security.yml @@ -119,6 +119,12 @@ - name: SETUP WINDOWS WORKSTATION hosts: security_windows + vars: + ansible_user: Administrator + ansible_password: "{{ windows_password }}" + ansible_port: 5986 + ansible_connection: winrm + ansible_winrm_server_cert_validation: ignore roles: - role: windows_ws_setup From 4fad47659c76314dcfb51d09f1ab21ee17442a89 Mon Sep 17 00:00:00 2001 From: ipvsean Date: Tue, 28 Apr 2020 20:10:44 -0400 Subject: [PATCH 05/18] fixing ansibe.cfg --- provisioner/ansible.cfg | 6 +++--- .../roles/manage_ec2_instances/tasks/inventory/dynamic.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/provisioner/ansible.cfg b/provisioner/ansible.cfg index 3fa51be1b..84b626658 100644 --- a/provisioner/ansible.cfg +++ b/provisioner/ansible.cfg @@ -8,11 +8,11 @@ # finds first [defaults] -localhost_warning: false +localhost_warning = false interpreter_python = auto_silent stdout_callback = yaml -inventory = hosts -forks = 50 +inventory = workshop_inventory +forks = 50 host_key_checking = False retry_files_enabled = False no_target_syslog = False diff --git a/provisioner/roles/manage_ec2_instances/tasks/inventory/dynamic.yml b/provisioner/roles/manage_ec2_instances/tasks/inventory/dynamic.yml index 4d67e29c0..fc90e5e91 100644 --- a/provisioner/roles/manage_ec2_instances/tasks/inventory/dynamic.yml +++ b/provisioner/roles/manage_ec2_instances/tasks/inventory/dynamic.yml @@ -2,7 +2,7 @@ - name: customize dynamic inventory script template: src: "aws_ec2.j2" - dest: "{{playbook_dir}}/aws_ec2.yml" + dest: "{{playbook_dir}}/workshop_inventory/aws_ec2.yml" - name: debug it debug: From 2ae5e88d019c1424a4b477e987c9ee4dd5bc7a3d Mon Sep 17 00:00:00 2001 From: ipvsean Date: Tue, 28 Apr 2020 20:12:57 -0400 Subject: [PATCH 06/18] syncing fixes --- .gitignore | 1 + provisioner/roles/manage_ec2_instances/tasks/main.yml | 11 +++-------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index d4b4275f2..e898cff11 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ extra_vars.yml TODO TODO.md bak/ +provisioner/workshop_inventory/* *.BAK *.DS_Store *.pem diff --git a/provisioner/roles/manage_ec2_instances/tasks/main.yml b/provisioner/roles/manage_ec2_instances/tasks/main.yml index b062ec449..1f3e83575 100644 --- a/provisioner/roles/manage_ec2_instances/tasks/main.yml +++ b/provisioner/roles/manage_ec2_instances/tasks/main.yml @@ -2,12 +2,7 @@ - include_tasks: teardown.yml when: teardown|bool -- name: provision workshop in AWS public cloud - block: - - name: provision aws resources and instances - include_tasks: provision.yml - tags: provisioned - - - name: create instructor_inventory, and student files - include_tasks: create_inventory.yml +- name: provision aws resources and instances + include_tasks: provision.yml + tags: provisioned when: not teardown|bool From 9505482b8fe97d176f6950b9c6ab9bcb1940b1d4 Mon Sep 17 00:00:00 2001 From: ipvsean Date: Tue, 28 Apr 2020 21:36:30 -0400 Subject: [PATCH 07/18] fixing bugs with control node role --- provisioner/ansible.cfg | 9 --------- provisioner/provision_lab.yml | 2 ++ provisioner/roles/aws_dns/tasks/tower.yml | 2 +- provisioner/roles/control_node/tasks/main.yml | 8 ++++---- provisioner/roles/control_node/templates/etchosts.j2 | 11 +++++++++++ .../roles/control_node/templates/inventory/rhel_90.j2 | 1 + 6 files changed, 19 insertions(+), 14 deletions(-) create mode 100644 provisioner/roles/control_node/templates/etchosts.j2 create mode 100644 provisioner/roles/control_node/templates/inventory/rhel_90.j2 diff --git a/provisioner/ansible.cfg b/provisioner/ansible.cfg index 84b626658..156fcc900 100644 --- a/provisioner/ansible.cfg +++ b/provisioner/ansible.cfg @@ -1,12 +1,3 @@ -# config file for ansible -- http://ansible.com/ -# ============================================== - -# nearly all parameters can be overridden in ansible-playbook -# or with command line flags. ansible will read ANSIBLE_CONFIG, -# ansible.cfg in the current working directory, .ansible.cfg in -# the home directory or /etc/ansible/ansible.cfg, whichever it -# finds first - [defaults] localhost_warning = false interpreter_python = auto_silent diff --git a/provisioner/provision_lab.yml b/provisioner/provision_lab.yml index f0a305b6f..01528e48b 100644 --- a/provisioner/provision_lab.yml +++ b/provisioner/provision_lab.yml @@ -207,6 +207,8 @@ when: - dns_type is defined - dns_type == "aws" + - towerinstall is defined + - towerinstall tags: control_node - name: Setup Amazon S3 Website for Student Login diff --git a/provisioner/roles/aws_dns/tasks/tower.yml b/provisioner/roles/aws_dns/tasks/tower.yml index d2c1e1a69..dedcf52bd 100644 --- a/provisioner/roles/aws_dns/tasks/tower.yml +++ b/provisioner/roles/aws_dns/tasks/tower.yml @@ -1,6 +1,6 @@ --- - name: CHANGE TOWER BASE URL - tower_settings: + awx.awx.tower_settings: name: TOWER_URL_BASE value: "https://{{username}}.{{ec2_name_prefix|lower}}.{{workshop_dns_zone}}" tower_verify_ssl: false diff --git a/provisioner/roles/control_node/tasks/main.yml b/provisioner/roles/control_node/tasks/main.yml index 32b19d3af..28c771750 100644 --- a/provisioner/roles/control_node/tasks/main.yml +++ b/provisioner/roles/control_node/tasks/main.yml @@ -43,8 +43,8 @@ group: "{{ username }}" - name: setup /etc/hosts file per student - copy: - src: "{{ playbook_dir }}/{{ec2_name_prefix}}/{{ username }}-etchosts.txt" + template: + src: "etchosts.j2" dest: "/etc/hosts" owner: "{{ username }}" group: "{{ username }}" @@ -112,8 +112,8 @@ path: /home/{{ username }}/lab_inventory - name: Put student inventory in proper spot - copy: - src: "{{ playbook_dir }}/{{ec2_name_prefix}}/{{ username }}-instances.txt" + template: + src: "{{role_path}}/templates/inventory/{{workshop_type}}.j2" dest: /home/{{ username }}/lab_inventory/hosts owner: "{{ username }}" group: "{{ username }}" diff --git a/provisioner/roles/control_node/templates/etchosts.j2 b/provisioner/roles/control_node/templates/etchosts.j2 new file mode 100644 index 000000000..019cdac3b --- /dev/null +++ b/provisioner/roles/control_node/templates/etchosts.j2 @@ -0,0 +1,11 @@ +127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 +::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 + +{% for vm in groups[username] %} +{% if hostvars[vm].short_name != "ansible" %} +{{ hostvars[vm].ansible_host }} {{ hostvars[vm].short_name }} +{% endif %} +{% endfor %} + +{% set control_node = ec2_name_prefix + '-' + username + '-ansible' %} +{{ hostvars[control_node].ansible_host }} {{username }}.{{ec2_name_prefix|lower}}.{{workshop_dns_zone}} {{ hostvars[control_node].short_name }} diff --git a/provisioner/roles/control_node/templates/inventory/rhel_90.j2 b/provisioner/roles/control_node/templates/inventory/rhel_90.j2 new file mode 100644 index 000000000..4991f8db2 --- /dev/null +++ b/provisioner/roles/control_node/templates/inventory/rhel_90.j2 @@ -0,0 +1 @@ +{% include "rhel.j2" %} From 83275100b424dd384f7901ab38065b0494de7003 Mon Sep 17 00:00:00 2001 From: ipvsean Date: Tue, 28 Apr 2020 22:00:19 -0400 Subject: [PATCH 08/18] Update dynamic.yml makesure workshop_inventory directory is present, failing zuul tests --- .../manage_ec2_instances/tasks/inventory/dynamic.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/provisioner/roles/manage_ec2_instances/tasks/inventory/dynamic.yml b/provisioner/roles/manage_ec2_instances/tasks/inventory/dynamic.yml index fc90e5e91..0f504fb76 100644 --- a/provisioner/roles/manage_ec2_instances/tasks/inventory/dynamic.yml +++ b/provisioner/roles/manage_ec2_instances/tasks/inventory/dynamic.yml @@ -1,13 +1,14 @@ --- +- name: Create workshop_inventory directory + file: + state: directory + path: "{{playbook_dir}}/workshop_inventory" + - name: customize dynamic inventory script template: src: "aws_ec2.j2" dest: "{{playbook_dir}}/workshop_inventory/aws_ec2.yml" -- name: debug it - debug: - msg: "{{ playbook_dir }}" - - name: refresh AWS inventory information meta: refresh_inventory From fcd5d7c10755e44504e2f63bdf7fe1f27961ad87 Mon Sep 17 00:00:00 2001 From: ipvsean Date: Wed, 29 Apr 2020 08:18:42 -0400 Subject: [PATCH 09/18] fixing CI to pass testing --- provisioner/tests/ci-windows.yml | 1 + provisioner/tests/rhel_verify.yml | 2 +- provisioner/tests/verify-f5.sh | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/provisioner/tests/ci-windows.yml b/provisioner/tests/ci-windows.yml index 324430368..671274363 100644 --- a/provisioner/tests/ci-windows.yml +++ b/provisioner/tests/ci-windows.yml @@ -1,3 +1,4 @@ --- workshop_type: windows ec2_region: us-west-1 +windows_password: 'Ansible+Red*Hat-Testing19!20' diff --git a/provisioner/tests/rhel_verify.yml b/provisioner/tests/rhel_verify.yml index 5678f2b93..2661106ac 100644 --- a/provisioner/tests/rhel_verify.yml +++ b/provisioner/tests/rhel_verify.yml @@ -1,6 +1,6 @@ --- - name: Check each student environment - hosts: all + hosts: control gather_facts: true tasks: diff --git a/provisioner/tests/verify-f5.sh b/provisioner/tests/verify-f5.sh index 6902ea613..5ff833308 100755 --- a/provisioner/tests/verify-f5.sh +++ b/provisioner/tests/verify-f5.sh @@ -5,7 +5,7 @@ set -eu DEPLOYMENT_NAME=$(grep 'ec2_name_prefix' provisioner/tests/ci-common.yml | cut -d' ' -f2) ADMIN_PASSWORD=$(grep 'admin_password' provisioner/tests/ci-common.yml | cut -d' ' -f2) -CONTROL_NODE_HOST=$(grep -A 1 control provisioner/${DEPLOYMENT_NAME}/student1-instances.txt | tail -n 1 | cut -d' ' -f 2 | cut -d'=' -f2) +CONTROL_NODE_HOST=$(grep -A 1 control provisioner/${DEPLOYMENT_NAME}/instructor_inventory.txt | tail -n 1 | cut -d' ' -f 2 | cut -d'=' -f2) RUN_ALL_PLAYBOOKS_CMD='find . -name "*.yml" -o -name "*.yaml" | grep -v "2.0" | sort | xargs -I {} bash -c "echo {} && ANSIBLE_FORCE_COLOR=true ansible-playbook {}"' From fec619de22a1d655cb81c1b5e3aa5bde38182d96 Mon Sep 17 00:00:00 2001 From: ipvsean Date: Wed, 29 Apr 2020 10:12:21 -0400 Subject: [PATCH 10/18] Update tower.yml making provisioner more stable --- provisioner/roles/aws_dns/tasks/tower.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/provisioner/roles/aws_dns/tasks/tower.yml b/provisioner/roles/aws_dns/tasks/tower.yml index dedcf52bd..a52a4878b 100644 --- a/provisioner/roles/aws_dns/tasks/tower.yml +++ b/provisioner/roles/aws_dns/tasks/tower.yml @@ -1,5 +1,5 @@ --- -- name: CHANGE TOWER BASE URL +- name: change ansible tower base URL awx.awx.tower_settings: name: TOWER_URL_BASE value: "https://{{username}}.{{ec2_name_prefix|lower}}.{{workshop_dns_zone}}" @@ -7,6 +7,9 @@ tower_host: localhost tower_username: admin tower_password: "{{admin_password}}" + register: change_base_url + until: change_base_url is not failed + retries: 5 # directions found here https://certbot.eff.org/lets-encrypt/centosrhel8-other - name: Download and install certbot From 70da2aa3d1459cb37a989c0d8223e85c23ebe3cb Mon Sep 17 00:00:00 2001 From: ipvsean Date: Wed, 29 Apr 2020 14:22:31 -0400 Subject: [PATCH 11/18] Update pipeline.groovy fix f5 verification --- provisioner/tests/pipeline.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioner/tests/pipeline.groovy b/provisioner/tests/pipeline.groovy index 6e8569501..b843b9249 100644 --- a/provisioner/tests/pipeline.groovy +++ b/provisioner/tests/pipeline.groovy @@ -221,7 +221,7 @@ EOF } script { stage('F5-exercises') { - sh "cat provisioner/tqe-f5-tower${DOTLESS_TOWER_VERSION}-${env.BUILD_ID}-${SHORTENED_ANSIBLE_VERSION}/student1-instances.txt | grep -A 1 control | tail -n 1 | cut -d' ' -f 2 | cut -d'=' -f2 | tee control_host" + sh "cat provisioner/tqe-f5-tower${DOTLESS_TOWER_VERSION}-${env.BUILD_ID}-${SHORTENED_ANSIBLE_VERSION}/instructor_inventory.txt | grep -A 1 control | tail -n 1 | cut -d' ' -f 2 | cut -d'=' -f2 | tee control_host" CONTROL_NODE_HOST = readFile('control_host').trim() RUN_ALL_PLAYBOOKS = 'find . -name "*.yml" -o -name "*.yaml" | grep -v "2.0" | sort | xargs -I {} bash -c "echo {} && ANSIBLE_FORCE_COLOR=true ansible-playbook {}"' sh "sshpass -p '${ADMIN_PASSWORD}' ssh -o StrictHostKeyChecking=no student1@${CONTROL_NODE_HOST} 'cd networking-workshop && ${RUN_ALL_PLAYBOOKS}'" From 57b0448f1e077117a104679256d7db5e33ce9bc6 Mon Sep 17 00:00:00 2001 From: ipvsean Date: Wed, 29 Apr 2020 17:10:01 -0400 Subject: [PATCH 12/18] fixing f5 exercise testing --- provisioner/control_host | 1 + provisioner/tests/gating.groovy | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 provisioner/control_host diff --git a/provisioner/control_host b/provisioner/control_host new file mode 100644 index 000000000..3ebe3b167 --- /dev/null +++ b/provisioner/control_host @@ -0,0 +1 @@ +18.220.232.182 diff --git a/provisioner/tests/gating.groovy b/provisioner/tests/gating.groovy index 2fcaa29db..a856b30c0 100644 --- a/provisioner/tests/gating.groovy +++ b/provisioner/tests/gating.groovy @@ -167,7 +167,7 @@ EOF } script { stage('F5-exercises') { - sh "cat provisioner/tqe-f5-tower${DOTLESS_TOWER_VERSION}-${env.BRANCH_NAME}-${env.BUILD_ID}/student1-instances.txt | grep -A 1 control | tail -n 1 | cut -d' ' -f 2 | cut -d'=' -f2 | tee control_host" + sh "cat provisioner/tqe-f5-tower${DOTLESS_TOWER_VERSION}-${env.BRANCH_NAME}-${env.BUILD_ID}/instructor_inventory.txt | grep -A 1 control | tail -n 1 | cut -d' ' -f 2 | cut -d'=' -f2 | tee control_host" CONTROL_NODE_HOST = readFile('control_host').trim() RUN_ALL_PLAYBOOKS = 'find . -name "*.yml" -o -name "*.yaml" | grep -v "2.0" | sort | xargs -I {} bash -c "echo {} && ANSIBLE_FORCE_COLOR=true ansible-playbook {}"' sh "sshpass -p '${ADMIN_PASSWORD}' ssh -o StrictHostKeyChecking=no student1@${CONTROL_NODE_HOST} 'cd networking-workshop && ${RUN_ALL_PLAYBOOKS}'" From 29c3c237eb035147ccf486a10dd041b4c881af17 Mon Sep 17 00:00:00 2001 From: ipvsean Date: Wed, 29 Apr 2020 22:43:00 -0400 Subject: [PATCH 13/18] Update provision_lab.yml robustness for ansible-galaxy --- provisioner/provision_lab.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/provisioner/provision_lab.yml b/provisioner/provision_lab.yml index 01528e48b..9a79bb088 100644 --- a/provisioner/provision_lab.yml +++ b/provisioner/provision_lab.yml @@ -154,6 +154,9 @@ loop: - "ansible-galaxy collection install ansible.product_demos" - "ansible-galaxy collection install awx.awx" + register: galaxy + until: galaxy is not failed + retries: 5 - name: Create lab instances in AWS hosts: localhost From b3e5a9ec9f463bc000286083fb9d7c744bc3f62b Mon Sep 17 00:00:00 2001 From: ipvsean Date: Thu, 30 Apr 2020 08:18:34 -0400 Subject: [PATCH 14/18] syncing more fixes for security_veriy.yml also removing old templates --- .../templates/devops_instances.txt.j2 | 36 -------- .../templates/etchosts_devops.j2 | 32 ------- .../templates/etchosts_f5.j2 | 26 ------ .../templates/etchosts_networking.j2 | 28 ------ .../templates/etchosts_rhel.j2 | 26 ------ .../templates/etchosts_security.j2 | 44 ---------- .../templates/etchosts_windows.j2 | 30 ------- .../templates/f5_instances.txt.j2 | 32 ------- .../templates/instances.txt.j2 | 30 ------- .../templates/networking_instances.txt.j2 | 87 ------------------- .../templates/skylight_gitlab_userdata.j2 | 6 -- provisioner/tests/security_verify.yml | 13 +-- 12 files changed, 7 insertions(+), 383 deletions(-) delete mode 100644 provisioner/roles/manage_ec2_instances/templates/devops_instances.txt.j2 delete mode 100644 provisioner/roles/manage_ec2_instances/templates/etchosts_devops.j2 delete mode 100644 provisioner/roles/manage_ec2_instances/templates/etchosts_f5.j2 delete mode 100644 provisioner/roles/manage_ec2_instances/templates/etchosts_networking.j2 delete mode 100644 provisioner/roles/manage_ec2_instances/templates/etchosts_rhel.j2 delete mode 100644 provisioner/roles/manage_ec2_instances/templates/etchosts_security.j2 delete mode 100644 provisioner/roles/manage_ec2_instances/templates/etchosts_windows.j2 delete mode 100644 provisioner/roles/manage_ec2_instances/templates/f5_instances.txt.j2 delete mode 100644 provisioner/roles/manage_ec2_instances/templates/instances.txt.j2 delete mode 100644 provisioner/roles/manage_ec2_instances/templates/networking_instances.txt.j2 delete mode 100755 provisioner/roles/manage_ec2_instances/templates/skylight_gitlab_userdata.j2 diff --git a/provisioner/roles/manage_ec2_instances/templates/devops_instances.txt.j2 b/provisioner/roles/manage_ec2_instances/templates/devops_instances.txt.j2 deleted file mode 100644 index ff22132a6..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/devops_instances.txt.j2 +++ /dev/null @@ -1,36 +0,0 @@ -[all:vars] -ansible_user=student{{ item }} -ansible_ssh_pass={{ admin_password }} -{% if ssh_port is defined %} -ansible_port={{ ssh_port }} -{% endif %} - -[sitea] -{% for vm in node1_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.tags.short_name }} ansible_host={{ vm.private_ip_address }} -{% endif %} -{% endfor %} -{% for vm in node2_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{vm.tags.short_name }} ansible_host={{ vm.private_ip_address }} -{% endif %} -{% endfor %} -[siteb] -{% for vm in node3_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{vm.tags.short_name }} ansible_host={{ vm.private_ip_address }} -{% endif %} -{% endfor %} -{% for vm in node4_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{vm.tags.short_name }} ansible_host={{ vm.private_ip_address }} -{% endif %} -{% endfor %} - -[control] -{% for vm in ansible_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.tags.Name | regex_replace('.*-([\\w]*)', '\\1') }} ansible_host={{ vm.public_ip_address }} -{% endif %} -{% endfor %} diff --git a/provisioner/roles/manage_ec2_instances/templates/etchosts_devops.j2 b/provisioner/roles/manage_ec2_instances/templates/etchosts_devops.j2 deleted file mode 100644 index 3d2d92b11..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/etchosts_devops.j2 +++ /dev/null @@ -1,32 +0,0 @@ -127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 -::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 - -{% for vm in node1_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{ vm.tags.short_name }} -{% endif %} -{% endfor %} - -{% for vm in node2_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{vm.tags.short_name}} -{% endif %} -{% endfor %} - -{% for vm in node3_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{vm.tags.short_name}} -{% endif %} -{% endfor %} - -{% for vm in node4_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{vm.tags.short_name}} -{% endif %} -{% endfor %} - -{% for vm in ansible_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{vm.tags.Student}}.{{ec2_name_prefix|lower}}.{{workshop_dns_zone}} {{ vm.tags.short_name }} -{% endif %} -{% endfor %} diff --git a/provisioner/roles/manage_ec2_instances/templates/etchosts_f5.j2 b/provisioner/roles/manage_ec2_instances/templates/etchosts_f5.j2 deleted file mode 100644 index 442b82373..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/etchosts_f5.j2 +++ /dev/null @@ -1,26 +0,0 @@ -127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 -::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 - -{% for vm in f5_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{ vm.tags.short_name }} -{% endif %} -{% endfor %} - -{% for vm in node1_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{ vm.tags.short_name }} -{% endif %} -{% endfor %} - -{% for vm in node2_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{vm.tags.short_name}} -{% endif %} -{% endfor %} - -{% for vm in ansible_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{vm.tags.Student}}.{{ec2_name_prefix|lower}}.{{workshop_dns_zone}} {{ vm.tags.short_name }} -{% endif %} -{% endfor %} diff --git a/provisioner/roles/manage_ec2_instances/templates/etchosts_networking.j2 b/provisioner/roles/manage_ec2_instances/templates/etchosts_networking.j2 deleted file mode 100644 index 519e4a018..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/etchosts_networking.j2 +++ /dev/null @@ -1,28 +0,0 @@ -127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 -::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 - -{% for vm in rtr1_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{ vm.tags.short_name }} -{% endif %} -{% endfor %} -{% for vm in rtr2_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{ vm.tags.short_name }} -{% endif %} -{% endfor %} -{% for vm in rtr3_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{ vm.tags.short_name }} -{% endif %} -{% endfor %} -{% for vm in rtr4_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{ vm.tags.short_name }} -{% endif %} -{% endfor %} -{% for vm in ansible_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{vm.tags.Student}}.{{ec2_name_prefix|lower}}.{{workshop_dns_zone}} {{ vm.tags.short_name }} -{% endif %} -{% endfor %} diff --git a/provisioner/roles/manage_ec2_instances/templates/etchosts_rhel.j2 b/provisioner/roles/manage_ec2_instances/templates/etchosts_rhel.j2 deleted file mode 100644 index ef1dd00d2..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/etchosts_rhel.j2 +++ /dev/null @@ -1,26 +0,0 @@ -127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 -::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 - -{% for vm in node1_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{ vm.tags.short_name }} -{% endif %} -{% endfor %} - -{% for vm in node2_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{vm.tags.short_name}} -{% endif %} -{% endfor %} - -{% for vm in node3_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{vm.tags.short_name}} -{% endif %} -{% endfor %} - -{% for vm in ansible_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{vm.tags.Student}}.{{ec2_name_prefix|lower}}.{{workshop_dns_zone}} {{ vm.tags.short_name }} -{% endif %} -{% endfor %} diff --git a/provisioner/roles/manage_ec2_instances/templates/etchosts_security.j2 b/provisioner/roles/manage_ec2_instances/templates/etchosts_security.j2 deleted file mode 100644 index 45847a032..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/etchosts_security.j2 +++ /dev/null @@ -1,44 +0,0 @@ -127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 -::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 - -{% for vm in windows_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{ vm.tags.short_name }} -{% endif %} -{% endfor %} - -{% for vm in qradar_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{ vm.tags.short_name }} -{% endif %} -{% endfor %} - -{% for vm in attacker_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{vm.tags.short_name}} -{% endif %} -{% endfor %} - -{% for vm in checkpoint_gw_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{vm.tags.short_name}} -{% endif %} -{% endfor %} - -{% for vm in checkpoint_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{vm.tags.short_name}} -{% endif %} -{% endfor %} - -{% for vm in snort_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{vm.tags.short_name}} -{% endif %} -{% endfor %} - -{% for vm in ansible_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{vm.tags.Student}}.{{ec2_name_prefix|lower}}.{{workshop_dns_zone}} {{ vm.tags.short_name }} -{% endif %} -{% endfor %} diff --git a/provisioner/roles/manage_ec2_instances/templates/etchosts_windows.j2 b/provisioner/roles/manage_ec2_instances/templates/etchosts_windows.j2 deleted file mode 100644 index 514e514ab..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/etchosts_windows.j2 +++ /dev/null @@ -1,30 +0,0 @@ -127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 -::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 - -{% if instance1_node_facts.instances is defined %} -{% for vm in instance1_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{ vm.tags.short_name }} -{% endif %} -{% endfor %} -{% endif %} - -{% if instance2_node_facts is defined %} -{% if instance2_node_facts.instances is defined %} -{% for vm in instance2_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{ vm.tags.short_name }} -{% endif %} -{% endfor %} -{% endif %} -{% endif %} - -{% for vm in gitlab_node_facts.instances %} -{{ vm.public_ip_address }} {{ vm.tags.short_name }} -{% endfor %} - -{% for vm in ansible_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.public_ip_address }} {{vm.tags.Student}}.{{ec2_name_prefix|lower}}.{{workshop_dns_zone}} {{ vm.tags.short_name }} -{% endif %} -{% endfor %} diff --git a/provisioner/roles/manage_ec2_instances/templates/f5_instances.txt.j2 b/provisioner/roles/manage_ec2_instances/templates/f5_instances.txt.j2 deleted file mode 100644 index e447362ea..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/f5_instances.txt.j2 +++ /dev/null @@ -1,32 +0,0 @@ -[all:vars] -ansible_user=student{{ item }} -ansible_ssh_pass={{ admin_password }} -{% if ssh_port is defined %} -ansible_port={{ ssh_port }} -{% endif %} - -[lb] -{% for vm in f5_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -f5 ansible_host={{ vm.public_ip_address }} ansible_user=admin private_ip={{ vm.private_ip_address }} ansible_ssh_pass={{admin_password}} -{% endif %} -{% endfor %} - -[control] -{% for vm in ansible_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -ansible ansible_host={{ vm.public_ip_address }} ansible_user={{ vm.tags.username }} private_ip={{ vm.private_ip_address }} -{% endif %} -{% endfor %} - -[web] -{% for vm in node1_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -node1 ansible_host={{ vm.public_ip_address }} ansible_user={{ vm.tags.username }} private_ip={{ vm.private_ip_address }} -{% endif %} -{% endfor %} -{% for vm in node2_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -node2 ansible_host={{ vm.public_ip_address }} ansible_user={{ vm.tags.username }} private_ip={{ vm.private_ip_address }} -{% endif %} -{% endfor %} diff --git a/provisioner/roles/manage_ec2_instances/templates/instances.txt.j2 b/provisioner/roles/manage_ec2_instances/templates/instances.txt.j2 deleted file mode 100644 index a8a38361e..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/instances.txt.j2 +++ /dev/null @@ -1,30 +0,0 @@ -[all:vars] -ansible_user=student{{ item }} -ansible_ssh_pass={{ admin_password }} -{% if ssh_port is defined %} -ansible_port={{ ssh_port }} -{% endif %} - -[web] -{% for vm in node1_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.tags.Name | regex_replace('.*-([\\w]*)', '\\1') }} ansible_host={{ vm.public_ip_address }} -{% endif %} -{% endfor %} -{% for vm in node2_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.tags.Name | regex_replace('.*-([\\w]*)', '\\1') }} ansible_host={{ vm.public_ip_address }} -{% endif %} -{% endfor %} -{% for vm in node3_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.tags.Name | regex_replace('.*-([\\w]*)', '\\1') }} ansible_host={{ vm.public_ip_address }} -{% endif %} -{% endfor %} - -[control] -{% for vm in ansible_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.tags.Name | regex_replace('.*-([\\w]*)', '\\1') }} ansible_host={{ vm.public_ip_address }} -{% endif %} -{% endfor %} diff --git a/provisioner/roles/manage_ec2_instances/templates/networking_instances.txt.j2 b/provisioner/roles/manage_ec2_instances/templates/networking_instances.txt.j2 deleted file mode 100644 index 620d05117..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/networking_instances.txt.j2 +++ /dev/null @@ -1,87 +0,0 @@ -[all:vars] -ansible_ssh_private_key_file=/home/student{{item}}/.ssh/aws-private.pem - -[routers:children] -{% if network_type == "multivendor" or network_type == "cisco" %} -cisco -{% endif %} -{% if network_type == "multivendor" or network_type == "juniper" %} -juniper -{% endif %} -{% if network_type == "multivendor" or network_type == "arista" %} -arista -{% endif %} - -{% if network_type == "multivendor" or network_type == "cisco" %} -[cisco] -{% endif %} -{% if network_type == "arista" %} -[arista] -{% endif %} -{% if network_type == "juniper" %} -[juniper] -{% endif %} -{% for vm in rtr1_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.tags.short_name }} ansible_host={{ vm.public_ip_address }} private_ip={{ vm.private_ip_address }} -{% endif %} -{% endfor %} -{% if network_type == "multivendor" %} -[arista] -{% endif %} -{% for vm in rtr2_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.tags.short_name }} ansible_host={{ vm.public_ip_address }} private_ip={{ vm.private_ip_address }} -{% endif %} -{% endfor %} -{% for vm in rtr4_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.tags.short_name }} ansible_host={{ vm.public_ip_address }} private_ip={{ vm.private_ip_address }} -{% endif %} -{% endfor %} -{% if network_type == "multivendor" %} -[juniper] -{% endif %} -{% for vm in rtr3_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.tags.short_name }} ansible_host={{ vm.public_ip_address }} private_ip={{ vm.private_ip_address }} -{% endif %} -{% endfor %} - -{% if network_type == "multivendor" or network_type == "cisco" %} -[cisco:vars] -ansible_user=ec2-user -ansible_network_os=ios -ansible_connection=network_cli -{% endif %} - -{% if network_type == "multivendor" or network_type == "juniper" %} -[juniper:vars] -ansible_user=ec2-user -ansible_network_os=junos -ansible_connection=netconf -{% endif %} - -{% if network_type == "multivendor" or network_type == "arista" %} -[arista:vars] -ansible_user=ec2-user -ansible_network_os=eos -ansible_connection=network_cli -ansible_become=true -ansible_become_method=enable -{% endif %} - -[dc1] -rtr1 -rtr3 - -[dc2] -rtr2 -rtr4 - -[control] -{% for vm in ansible_node_facts.instances %} -{% if 'student' + item == vm.tags.Student %} -{{ vm.tags.short_name }} ansible_host={{ vm.public_ip_address }} ansible_user={{ vm.tags.Student }} private_ip={{ vm.private_ip_address }} ansible_password={{admin_password}} -{% endif %} -{% endfor %} diff --git a/provisioner/roles/manage_ec2_instances/templates/skylight_gitlab_userdata.j2 b/provisioner/roles/manage_ec2_instances/templates/skylight_gitlab_userdata.j2 deleted file mode 100755 index e9b1dd1cf..000000000 --- a/provisioner/roles/manage_ec2_instances/templates/skylight_gitlab_userdata.j2 +++ /dev/null @@ -1,6 +0,0 @@ -#cloud-config -hostname: "gitlab" -fqdn: "gitlab.{{ workshop_dns_zone }}" -manage_etc_hosts: true -chpasswd: { expire: False } -ssh_pwauth: True diff --git a/provisioner/tests/security_verify.yml b/provisioner/tests/security_verify.yml index 1a1694fb5..a319880a1 100644 --- a/provisioner/tests/security_verify.yml +++ b/provisioner/tests/security_verify.yml @@ -9,8 +9,9 @@ loop: - 'ibm.qradar' +# run on everything except the checkpoint_gw - name: Check each student environment - hosts: all + hosts: all:!checkpoint gather_facts: true tasks: @@ -118,7 +119,7 @@ - name: Test NGFW access, step 1/3 - login, get SID uri: - url: "https://{{ hostvars[inventory_hostname|regex_replace('ansible', 'checkpoint')]['ansible_host'] }}/web_api/login" + url: "https://{{ hostvars[inventory_hostname|regex_replace('ansible', 'checkpoint_mgmt')]['ansible_host'] }}/web_api/login" method: POST body: user: admin @@ -132,7 +133,7 @@ - name: Test NGFW access, step 2/3 - Get NGFW data uri: - url: "https://{{ hostvars[inventory_hostname|regex_replace('ansible', 'checkpoint')]['ansible_host'] }}/web_api/show-simple-gateway" + url: "https://{{ hostvars[inventory_hostname|regex_replace('ansible', 'checkpoint_mgmt')]['ansible_host'] }}/web_api/show-simple-gateway" validate_certs: false method: POST headers: @@ -160,7 +161,7 @@ - name: Validate eth1, step 1/3 - login, get SID uri: - url: "https://{{ hostvars[inventory_hostname|regex_replace('ansible', 'checkpoint')]['ansible_host'] }}/web_api/login" + url: "https://{{ hostvars[inventory_hostname|regex_replace('ansible', 'checkpoint_mgmt')]['ansible_host'] }}/web_api/login" method: POST body: user: admin @@ -174,7 +175,7 @@ - name: Validate eth1, step 2/3 - Launch script uri: - url: "https://{{ hostvars[inventory_hostname|regex_replace('ansible', 'checkpoint')]['ansible_host'] }}/web_api/run-script" + url: "https://{{ hostvars[inventory_hostname|regex_replace('ansible', 'checkpoint_mgmt')]['ansible_host'] }}/web_api/run-script" validate_certs: false method: POST headers: @@ -191,7 +192,7 @@ - name: Validate eth1, step 3/3 - Verify script return values uri: - url: "https://{{ hostvars[inventory_hostname|regex_replace('ansible', 'checkpoint')]['ansible_host'] }}/web_api/show-task" + url: "https://{{ hostvars[inventory_hostname|regex_replace('ansible', 'checkpoint_mgmt')]['ansible_host'] }}/web_api/show-task" validate_certs: false method: POST headers: From a187a6c37142caa432516374592d16c3a4535977 Mon Sep 17 00:00:00 2001 From: ipvsean Date: Thu, 30 Apr 2020 09:03:26 -0400 Subject: [PATCH 15/18] Create skylight_gitlab_userdata.j2 fix windows workshop, accidentally removed this template --- .../templates/skylight_gitlab_userdata.j2 | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 provisioner/roles/manage_ec2_instances/templates/skylight_gitlab_userdata.j2 diff --git a/provisioner/roles/manage_ec2_instances/templates/skylight_gitlab_userdata.j2 b/provisioner/roles/manage_ec2_instances/templates/skylight_gitlab_userdata.j2 new file mode 100644 index 000000000..1111cfd86 --- /dev/null +++ b/provisioner/roles/manage_ec2_instances/templates/skylight_gitlab_userdata.j2 @@ -0,0 +1,6 @@ +#cloud-config +hostname: "gitlab" +fqdn: "gitlab.{{ workshop_dns_zone }}" +manage_etc_hosts: true +chpasswd: { expire: False } +ssh_pwauth: True From 043f2c0e7d756c74ee28444d245a1556179efbe7 Mon Sep 17 00:00:00 2001 From: ipvsean Date: Thu, 30 Apr 2020 10:43:43 -0400 Subject: [PATCH 16/18] fixing security check --- provisioner/roles/code_server/tasks/codeserver.yml | 3 +++ .../roles/manage_ec2_instances/templates/aws_ec2.j2 | 2 +- provisioner/tests/security_verify.yml | 8 +++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/provisioner/roles/code_server/tasks/codeserver.yml b/provisioner/roles/code_server/tasks/codeserver.yml index 8477e1877..4f766fd8d 100644 --- a/provisioner/roles/code_server/tasks/codeserver.yml +++ b/provisioner/roles/code_server/tasks/codeserver.yml @@ -87,6 +87,9 @@ - hnw.vscode-auto-open-markdown-preview-0.0.4.vsix - vscoss.vscode-ansible-0.5.2.vsix ignore_errors: true + register: install_extension + until: install_extension is not failed + retries: 5 - name: start code-server service service: diff --git a/provisioner/roles/manage_ec2_instances/templates/aws_ec2.j2 b/provisioner/roles/manage_ec2_instances/templates/aws_ec2.j2 index 3984aa7b6..5f577b310 100644 --- a/provisioner/roles/manage_ec2_instances/templates/aws_ec2.j2 +++ b/provisioner/roles/manage_ec2_instances/templates/aws_ec2.j2 @@ -24,7 +24,7 @@ groups: security: "'security' in tags.Workshop_type" security_connection_check: attack snort qradar snort: "'snort' in tags.short_name" - checkpoint: "'checkpoint_gw' in tags.short_name" + # checkpoint: "'checkpoint_gw' in tags.short_name" checkpoint_mgmt: "'checkpoint_mgmt' in tags.short_name" security_windows: "'windows_ws' in tags.short_name" {% for n in range(1, student_total + 1) %} diff --git a/provisioner/tests/security_verify.yml b/provisioner/tests/security_verify.yml index a319880a1..f0fee7c99 100644 --- a/provisioner/tests/security_verify.yml +++ b/provisioner/tests/security_verify.yml @@ -11,7 +11,7 @@ # run on everything except the checkpoint_gw - name: Check each student environment - hosts: all:!checkpoint + hosts: all gather_facts: true tasks: @@ -210,6 +210,12 @@ - name: QRadar block: + - name: print out qradar information for debugging + debug: + msg: + - "URL: https://{{ hostvars[inventory_hostname]['ansible_host'] }}/api/help/endpoints/1" + - "USER: {{ hostvars[inventory_hostname]['ansible_user'] }}" + - "PASSWORD: {{ hostvars[inventory_hostname]['ansible_httpapi_pass'] }}" - name: Check if QRadar API is available uri: From 311de529701a5c5c4ad0715c2eb0cfe443d5854e Mon Sep 17 00:00:00 2001 From: ipvsean Date: Thu, 30 Apr 2020 21:49:37 -0400 Subject: [PATCH 17/18] Update instructor-security.j2 fixing username with suggestino from @liquidat --- .../roles/manage_ec2_instances/templates/instructor-security.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioner/roles/manage_ec2_instances/templates/instructor-security.j2 b/provisioner/roles/manage_ec2_instances/templates/instructor-security.j2 index 7561eba6b..cb48790f1 100644 --- a/provisioner/roles/manage_ec2_instances/templates/instructor-security.j2 +++ b/provisioner/roles/manage_ec2_instances/templates/instructor-security.j2 @@ -13,7 +13,7 @@ {% if security_console == 'qradar' %} {% for vm in groups.qradar %} -{{ hostvars[vm].username }}-{{ hostvars[vm].short_name }} ansible_host={{ hostvars[vm].ansible_host }} ansible_user={{ hostvars[vm].ansible_user }} private_ip={{ hostvars[vm].private_ip }} ansible_httpapi_pass="Ansible1!" ansible_connection=httpapi ansible_httpapi_use_ssl=yes ansible_httpapi_validate_certs=False ansible_network_os=ibm.qradar.qradar +{{ hostvars[vm].username }}-{{ hostvars[vm].short_name }} ansible_host={{ hostvars[vm].ansible_host }} ansible_user=admin private_ip={{ hostvars[vm].private_ip }} ansible_httpapi_pass="Ansible1!" ansible_connection=httpapi ansible_httpapi_use_ssl=yes ansible_httpapi_validate_certs=False ansible_network_os=ibm.qradar.qradar {% endfor %} {% endif %} From 2d3683c3a96621f3571d8f39a15783923b132cba Mon Sep 17 00:00:00 2001 From: ipvsean Date: Thu, 30 Apr 2020 23:41:03 -0400 Subject: [PATCH 18/18] Update security_verify.yml --- provisioner/tests/security_verify.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/provisioner/tests/security_verify.yml b/provisioner/tests/security_verify.yml index f0fee7c99..2c502854c 100644 --- a/provisioner/tests/security_verify.yml +++ b/provisioner/tests/security_verify.yml @@ -11,7 +11,7 @@ # run on everything except the checkpoint_gw - name: Check each student environment - hosts: all + hosts: all:!*checkpoint_gw gather_facts: true tasks: @@ -210,18 +210,12 @@ - name: QRadar block: - - name: print out qradar information for debugging - debug: - msg: - - "URL: https://{{ hostvars[inventory_hostname]['ansible_host'] }}/api/help/endpoints/1" - - "USER: {{ hostvars[inventory_hostname]['ansible_user'] }}" - - "PASSWORD: {{ hostvars[inventory_hostname]['ansible_httpapi_pass'] }}" - name: Check if QRadar API is available uri: url: "https://{{ hostvars[inventory_hostname]['ansible_host'] }}/api/help/endpoints/1" method: GET - user: "{{ hostvars[inventory_hostname]['ansible_user'] }}" + user: "admin" password: "{{ hostvars[inventory_hostname]['ansible_httpapi_pass'] }}" force_basic_auth: true headers:

    student{{number}} - Click to see login details