<rss xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:pingback="http://madskills.com/public/xml/rss/module/pingback/" xmlns:webfeeds="http://webfeeds.org/rss/1.0" version="2.0"><channel><webfeeds:logo>http://www.kevinlabranche.com/blog/images/zenicon.jpg</webfeeds:logo><webfeeds:analytics id="UA" engine="GoogleAnalytics" /><title>Kevin LaBranche</title><link>http://www.kevinlabranche.com/blog/</link><description>Developer advocate who enjoys developing engineering practices that make development efforts easier, more reliable, safer and thereby faster. </description><image><url>http://www.kevinlabranche.com/blog/images/zenicon.jpg</url><title>Kevin LaBranche</title><link>http://www.kevinlabranche.com/blog/</link></image><copyright>Kevin LaBranche</copyright><lastBuildDate>Thu, 06 Jul 2023 00:14:03 GMT</lastBuildDate><managingEditor>klabran@hotmail.com</managingEditor><webMaster>klabran@hotmail.com</webMaster><item><trackback:ping>http://www.kevinlabranche.com/blog/feed/trackback/1dd2f8e6-8d19-498b-9c29-16e10d6fa720</trackback:ping><pingback:server>http://www.kevinlabranche.com/blog/feed/pingback</pingback:server><pingback:target>http://www.kevinlabranche.com/blog/post/1dd2f8e6-8d19-498b-9c29-16e10d6fa720</pingback:target><dc:creator>Kevin LaBranche</dc:creator><title>Azure Podcast Guest– Leading Teams Through DevOps</title><guid isPermaLink="false">http://www.kevinlabranche.com/blog/post/1dd2f8e6-8d19-498b-9c29-16e10d6fa720</guid><link>http://www.kevinlabranche.com/blog/azure-podcast-guest-leading-teams-through-devops</link><pubDate>Thu, 06 Jul 2023 00:14:03 GMT</pubDate><description>&lt;div&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;I’ve been a part of an &lt;a href="https://clearmeasure.com/architect-forum/"&gt;Architect Forum&lt;/a&gt; that Clear Measure (&lt;a href="https://twitter.com/clearmeasure"&gt;@ClearMeasure&lt;/a&gt;) and Jeffrey Palermo (&lt;a href="https://twitter.com/jeffreypalermo"&gt;@Jeffreypalermo&lt;/a&gt;) have been hosting monthly.&amp;nbsp; With many conversations over these forums Jeffrey asked me to join him on his podcast.&amp;nbsp; I am honored to join the multitude of amazing alums on the podcast and definitely feel inadequate.&amp;nbsp; However, I won’t let that stop me and don’t let that stop you.&amp;nbsp; Show up, give your best, it will help someone and help you grow as well.&amp;nbsp; We all have something to share.&amp;nbsp; While we conversed about DevOps and build scripts the underlying thread is how I approach my architecture position with mentorship as a very strong and necessary component to success.&lt;/p&gt;&lt;p&gt;&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="en" dir="ltr"&gt;Listen to this Azure DevOps Podcast episode, Leading Teams through DevOps, with Kevin LaBranche. &lt;a href="https://t.co/Ai9fOiFa0X"&gt;https://t.co/Ai9fOiFa0X&lt;/a&gt; &lt;br&gt;&lt;br&gt;Learn what a build script is &lt;a href="https://twitter.com/jeffreypalermo?ref_src=twsrc%5Etfw"&gt;@jeffreypalermo&lt;/a&gt; &lt;a href="https://twitter.com/klabranche?ref_src=twsrc%5Etfw"&gt;@klabranche&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/azuredevopspodcast?src=hash&amp;amp;ref_src=twsrc%5Etfw"&gt;#azuredevopspodcast&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/devops?src=hash&amp;amp;ref_src=twsrc%5Etfw"&gt;#devops&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/buildscripts?src=hash&amp;amp;ref_src=twsrc%5Etfw"&gt;#buildscripts&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/API?src=hash&amp;amp;ref_src=twsrc%5Etfw"&gt;#API&lt;/a&gt; &lt;a href="https://t.co/X77GK9zqUL"&gt;pic.twitter.com/X77GK9zqUL&lt;/a&gt;&lt;/p&gt;&lt;p lang="en" dir="ltr"&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/244798f53304_E0BB/DevOpsEp251_2.jpg"&gt;&lt;img width="542" height="307" title="DevOpsEp251" style="display: inline; background-image: none;" alt="DevOpsEp251" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/244798f53304_E0BB/DevOpsEp251_thumb.jpg" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;— Clear Measure (@ClearMeasure) &lt;a href="https://twitter.com/ClearMeasure/status/1676316964094877697?ref_src=twsrc%5Etfw"&gt;July 4, 2023&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/div&gt;</description><category>Azure DevOps</category><category>Mentorship</category><category>Teams</category></item><item><trackback:ping>http://www.kevinlabranche.com/blog/feed/trackback/2c4c356a-0985-4102-9763-64d0a82b328d</trackback:ping><pingback:server>http://www.kevinlabranche.com/blog/feed/pingback</pingback:server><pingback:target>http://www.kevinlabranche.com/blog/post/2c4c356a-0985-4102-9763-64d0a82b328d</pingback:target><dc:creator>Kevin LaBranche</dc:creator><title>Conventions For Using Variables Groups In Azure DevOps</title><guid isPermaLink="false">http://www.kevinlabranche.com/blog/post/2c4c356a-0985-4102-9763-64d0a82b328d</guid><link>http://www.kevinlabranche.com/blog/conventions-for-using-variables-groups-in-azure-devops</link><pubDate>Thu, 15 Jun 2023 00:02:01 GMT</pubDate><description>&lt;div&gt;&lt;p&gt;Variable groups allow for sharing of configuration values that can be shared by all pipelines and releases in the same project.&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;br&gt;I have authored and maintained a few hundred Azure DevOps pipelines and releases over the last 3-5 years.&amp;nbsp; One of the conventions that I developed is around the use of variable groups.&amp;nbsp; &lt;br&gt;&lt;br&gt;The conventions I have set in our team provide for quick recognition of where a value is sourced from. The magic of variable groups matching on their name to replace a value in a configuration for a stage is too magical.&amp;nbsp; It’s super convenient until there’s a mismatch.&amp;nbsp; Not all tokenize replacement tasks are created equal either.&amp;nbsp; Some can be configured to error with mismatches, some can’t.&amp;nbsp; I have found this can easily be a source of wasted effort when troubleshooting an issue and/or worse the green release but a configuration value didn’t get replaced.&amp;nbsp; &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h4&gt;Conventions&lt;/h4&gt;&lt;p&gt;1. Add a prefix of your choosing to the name of each variable in the group.&amp;nbsp; &lt;/p&gt;&lt;p&gt;2. Explicitly map variables in your pipeline to them.&lt;/p&gt;&lt;p&gt;3. Use Release scope for variable groups mapped to stages.&lt;/p&gt;&lt;p&gt;4. How to override a variable group value.&lt;/p&gt;&lt;p&gt;5. Don’t lock a variable (make it a secret, pad lock icon) if it really doesn’t need to be hidden including logs.&lt;/p&gt;&lt;p&gt;6. If you have to lock a value consider instead using Azure Key Vault and link it to a variable group.&lt;/p&gt;&lt;p&gt;7. Create a developer wiki for the variable groups.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h4&gt;Add a prefix&lt;/h4&gt;&lt;p&gt;Adding a prefix like vg-variableName quickly allows a team member to know where this value is sourced from. If it’s just a value directly sourced in the pipeline variables we don’t add a prefix.&lt;/p&gt;&lt;p&gt;Bonus – If you have infrastructure based configurations such as what to name an IIS application, I recommend you prefix that as well.&amp;nbsp; &lt;/p&gt;&lt;p&gt;The prefixes or lack thereof is information that the team can use to more quickly assess pipeline variables.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h4&gt;Explicitly map&lt;/h4&gt;&lt;p&gt;By explicitly mapping our variable’s used in a variable group in our pipeline variables we remove the magic in replacing variables by name match for groups.&amp;nbsp; At a glance we know if variables are coming from a group and which one’s in the group we are using.&amp;nbsp; While this means some initial extra setup, it pays long term dividends in maintenance and troubleshooting. &lt;br&gt;&lt;br&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/c75ac67b7325_CFFD/variable-group-convention_2.png"&gt;&lt;img width="700" height="320" title="variable-group-convention" style="display: inline; background-image: none;" alt="variable-group-convention" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/c75ac67b7325_CFFD/variable-group-convention_thumb.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;br&gt;Our team chose symbols for brevity sake for our prefixes.&lt;/p&gt;&lt;p&gt;Underscore &lt;code&gt;_VariableName&lt;/code&gt; means it’s an IIS infrastructure configuration.&amp;nbsp; We deploy to many shared IIS environments with the Manage IIS and Deploy IIS tasks.&lt;/p&gt;&lt;p&gt;Caret &lt;code&gt;^VariableName&lt;/code&gt; means it comes from a variable group.&lt;/p&gt;&lt;p&gt;No prefix, it’s a local variable.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h4&gt;Use Release scope for variable groups mapped to stages&lt;/h4&gt;&lt;p&gt;I covered the next two conventions in an &lt;a href="http://www.kevinlabranche.com/blog/azure-devops-library-variables-stages-and-release-scopedid-you-know"&gt;earlier post&lt;/a&gt;.&amp;nbsp; If your variable groups are mapped to stages you can use one definition in your pipeline variables scoped to Release.&amp;nbsp; The stages will properly map.&lt;/p&gt;&lt;p&gt;This:&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/c75ac67b7325_CFFD/variable-group-pipelinevar-reference-to-release_2.png"&gt;&lt;img width="1008" height="96" title="variable-group-pipelinevar-reference-to-release" style="display: inline; background-image: none;" alt="variable-group-pipelinevar-reference-to-release" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/c75ac67b7325_CFFD/variable-group-pipelinevar-reference-to-release_thumb.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Not this:&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/c75ac67b7325_CFFD/variable-group-pipelinevar-reference-per-stage_2.png"&gt;&lt;img width="1006" height="146" title="variable-group-pipelinevar-reference-per-stage" style="display: inline; background-image: none;" alt="variable-group-pipelinevar-reference-per-stage" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/c75ac67b7325_CFFD/variable-group-pipelinevar-reference-per-stage_thumb.png" border="0"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h4&gt;How to override a variable group value &lt;/h4&gt;&lt;p&gt;Given a pipeline variable mapped to a variable group is using the Release scope one can then overwrite a stage by adding a staged scoped variable and value.&amp;nbsp; &lt;/p&gt;&lt;p&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/c75ac67b7325_CFFD/override-variable-group-release-value_2.png"&gt;&lt;img width="1025" height="85" title="override-variable-group-release-value" style="display: inline; background-image: none;" alt="override-variable-group-release-value" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/c75ac67b7325_CFFD/override-variable-group-release-value_thumb.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;br&gt;Doing this makes it obvious we are overriding and makes it easy to remove and go back to the variable groups value.&amp;nbsp; It also avoids having to look through logs to see what was used if one checked the “settable at release time” option.&amp;nbsp; &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h4&gt;Don’t lock&lt;/h4&gt;&lt;p&gt;As a starting point, don’t lock if it isn’t a secret.&amp;nbsp; If it is a secret, consider this carefully.&amp;nbsp; My reason isn’t to thwart proper security concerns.&amp;nbsp; I’ve seen too many times where the actual secret was not stored elsewhere and/or where it was stored elsewhere was no longer accurate.&amp;nbsp; Once it’s locked, you will never see it again in DevOps.&amp;nbsp; Troubleshooting this especially if one does not have a way to verify the configuration is rough.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h4&gt;Instead of locking use Azure Key Vault&lt;/h4&gt;&lt;p&gt;If you determine that the secret must be protected from casual viewing then please use a mechanism where the source feeds the variable group in a secure fashion.&amp;nbsp; Azure Key Vault is a great option for this.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h4&gt;Create a Developer Wiki for the Variable Groups&lt;/h4&gt;&lt;p&gt;Document the variable groups, share it, make sure the developers know about them.&amp;nbsp; I’ve ran into countless cases of pipeline variables that were setup with local values when a variable group existed. &lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;h4&gt;Learn More&lt;/h4&gt;&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/library/variable-groups?view=azure-devops&amp;amp;tabs=yaml"&gt;Add &amp;amp; Use variable groups&lt;/a&gt; &lt;br&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/library/variable-groups?view=azure-devops&amp;amp;tabs=yaml"&gt;Connecting Azure Key Vault to a variable group&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><category>Azure DevOps</category><category>deploy</category></item><item><trackback:ping>http://www.kevinlabranche.com/blog/feed/trackback/86993b07-3642-4eab-84d3-ee430ad881d2</trackback:ping><pingback:server>http://www.kevinlabranche.com/blog/feed/pingback</pingback:server><pingback:target>http://www.kevinlabranche.com/blog/post/86993b07-3642-4eab-84d3-ee430ad881d2</pingback:target><dc:creator>Kevin LaBranche</dc:creator><title>Node rookie discovery with NPM and NODE_ENV Production</title><guid isPermaLink="false">http://www.kevinlabranche.com/blog/post/86993b07-3642-4eab-84d3-ee430ad881d2</guid><link>http://www.kevinlabranche.com/blog/node-rookie-discovery-with-npm-and-nodeenv-production</link><pubDate>Thu, 08 Jun 2023 00:38:28 GMT</pubDate><description>&lt;div&gt;&lt;p&gt;To the node pro this post will not surprise.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;TLDR;&lt;/strong&gt; If NODE_ENV is set to production npm install and npm ci commands won’t install developer dependencies without passing --include=dev&amp;nbsp; (-–also=dev for npm older than v7).&amp;nbsp;&amp;nbsp; &lt;/p&gt;&lt;p&gt;&lt;br&gt;Why would you install developer dependencies in a Production environment?&amp;nbsp; Agree, 100%.&amp;nbsp; However, we do, gulp in particular. Not that I’m happy about it either.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;strong&gt;This functionality isn’t hidden but it is interesting how the instructions returned when we tried to use a developer dependency (gulp) ended up throwing the team for a loop and a few wasted cycles.&lt;/strong&gt;&lt;br&gt;&lt;/p&gt;&lt;h4&gt;&lt;br&gt;Story Time&lt;/h4&gt;&lt;p&gt;&lt;br&gt;It’s the age ole story of unwritten knowledge within the company, lack of expertise and lack of keeping things up to date. &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt; For us, it all started about seven years back during an ITS merger. The merger brought several disparate development teams into one and those teams used different development stacks (Java, Node, .Net).&amp;nbsp; The team members from those teams continued to maintain the apps until there were no more staff from those teams left.&amp;nbsp; Some apps were retired, some were rewritten to the chosen platform (.Net), some remained entombed in times gone by.&amp;nbsp; We did a fairly good job with documentation and even had recorded trainings from the staff leaving.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;Several years went by, apps happily running, a few glitches here and there with failures that we rectified through normal operational tasks. I’m a node newb and so are my fellow team members who had the task of updating one of these node applications.&amp;nbsp; The process to update the node app is not what I would consider modern.&amp;nbsp; There is no CI/CD chain. &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;The process to update the application on the server(s) was:&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;1. git pull to get the latest code.&lt;/p&gt;&lt;p&gt; 2.&amp;nbsp; npm ci&lt;/p&gt;&lt;p&gt;3.&amp;nbsp;&amp;nbsp; gulp to do all the needed minifying, bundling, copying, etc.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The typical local development machine steps or on a build server.&amp;nbsp; We happily updated the code needed and dutifully followed the process laid out on our development and test servers.&amp;nbsp; We moved to the production systems and when we ran the gulp step we got:&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt;&lt;br&gt;Local gulp not found in &amp;lt;application folder&amp;gt;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt;Try running: npm install gulp&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;br&gt;Scratching our heads, we compared dev/test and prod environments.&amp;nbsp; All seemed properly aligned.&amp;nbsp; We poured over our docs again not finding anything.&amp;nbsp; So we ran `npm install gulp` and we got: &lt;font color="#008000"&gt;updated 1 package in 26.605s&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;br&gt;To our surprise when we manually ran our `gulp` command we still got:&lt;font color="#ff0000"&gt; Local gulp not found in application folder Try running: npm install gulp&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;br&gt;So after more research and not finding anything we tried a force install.&amp;nbsp; No change. We could see that gulp wasn’t in the node_modules folder. &lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;h4&gt;The Aha Moment&lt;/h4&gt;&lt;p&gt;&lt;br&gt;We found in the &lt;a href="https://docs.npmjs.com/cli/v9/commands/npm-ci#omit"&gt;npm-ci docs in the omit parameter&lt;/a&gt; section the following two pieces of information.&lt;/p&gt;&lt;p&gt;&lt;em&gt;“The default state is to omit developer dependencies when NODE_ENV is set to production.”&lt;/em&gt;&amp;nbsp; Makes sense except it’s telling us it updated gulp?&lt;/p&gt;&lt;p&gt;&lt;em&gt;“Note that these dependencies are still resolved and added to the &lt;code&gt;package-lock.json&lt;/code&gt; or &lt;code&gt;npm-shrinkwrap.json&lt;/code&gt; file. They are just not physically installed on disk.”&lt;/em&gt;&lt;p&gt;&lt;br&gt;Totally not what we would expect to occur. Long story short, it resolved and updated the package-lock.json file but didn’t actually install. &lt;/p&gt;&lt;p&gt;&lt;br&gt;To install developer dependencies when in production mode pass `—include=dev` ( `-–also=dev` for npm older than v7) to have it REALLY install your developer dependencies to disk.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h4&gt;Learn More&lt;/h4&gt;&lt;p&gt;&lt;a title="https://docs.npmjs.com/cli/v9/commands/npm-ci#omit" href="https://docs.npmjs.com/cli/v9/commands/npm-ci#omit"&gt;https://docs.npmjs.com/cli/v9/commands/npm-ci#omit&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a title="https://worknme.wordpress.com/2021/12/19/how-to-install-dev-dependencies-in-node-env-production/" href="https://worknme.wordpress.com/2021/12/19/how-to-install-dev-dependencies-in-node-env-production/"&gt;https://worknme.wordpress.com/2021/12/19/how-to-install-dev-dependencies-in-node-env-production/&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><category>node</category><category>npm</category></item><item><trackback:ping>http://www.kevinlabranche.com/blog/feed/trackback/18911ffc-ef20-410e-babc-1cb5c92caba1</trackback:ping><pingback:server>http://www.kevinlabranche.com/blog/feed/pingback</pingback:server><pingback:target>http://www.kevinlabranche.com/blog/post/18911ffc-ef20-410e-babc-1cb5c92caba1</pingback:target><dc:creator>Kevin LaBranche</dc:creator><title>Azure DevOps - Library Variables, Stages and Release Scope–Did you know?</title><guid isPermaLink="false">http://www.kevinlabranche.com/blog/post/18911ffc-ef20-410e-babc-1cb5c92caba1</guid><link>http://www.kevinlabranche.com/blog/azure-devops-library-variables-stages-and-release-scopedid-you-know</link><pubDate>Thu, 01 Jun 2023 23:53:29 GMT</pubDate><description>&lt;div&gt;&lt;p&gt;Our team has over 100 different repositories (applications) with pipelines and release definitions in Azure DevOps.&amp;nbsp; We have many on-premise web servers that share hosting the applications.&amp;nbsp; With this setup we have taken advantage of using Library / Variable Groups to share settings that are reused.&amp;nbsp; In our team we recently “rediscovered” a little gem in how Azure DevOps works with library variables / variable groups, stages and Release scope.&amp;nbsp; I wanted to share as there was confusion in the team that was adding unnecessary pipeline variable setup.&lt;/p&gt;&lt;h4&gt;Pipeline Variables&lt;/h4&gt;&lt;p&gt;Given a three stage pipeline like dev-&amp;gt;test-&amp;gt;prod and you need to define a variable:&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/Library-Variables-and-Stages_B97A/three-stage-release_2.png"&gt;&lt;img width="773" height="219" title="three-stage-release" style="display: inline; background-image: none;" alt="three-stage-release" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/Library-Variables-and-Stages_B97A/three-stage-release_thumb[1].png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/Library-Variables-and-Stages_B97A/pipeline-variables_2.png"&gt;&lt;img width="776" height="124" title="pipeline-variables" style="display: inline; background-image: none;" alt="pipeline-variables" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/Library-Variables-and-Stages_B97A/pipeline-variables_thumb[1].png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;For a variable that is the same for all stages, use the Release scope. AppSettings.AppName for example in the above snippet doesn’t change for the stage.&lt;/p&gt;&lt;p&gt;For a variable that is different for each stage, define it three times, setting the value differently per the stage.&lt;/p&gt;&lt;p&gt;Straight forward, no fuss.&amp;nbsp; What about variables groups?&lt;/p&gt;&lt;h4&gt;Variable Groups&lt;/h4&gt;&lt;p&gt;Every release definition of ours has variable groups that describe the iis deployment information such as the root folder path for where we “install” the application folder and other IIS information that we have normalized for all the applications on that host (deployment group and/or environment).&amp;nbsp; We also have settings for our single sign on system that all the applications use and it’s settings for the various stages (dev/test/prod).&lt;/p&gt;&lt;p&gt;If the information for each stage does not change then we could pick the Release Scope when we setup the Variable Group.&amp;nbsp; Straight forward, no fuss.&lt;/p&gt;&lt;p&gt;If you are using variable groups scoped to a stage then do you have to declare the variable per stage, three times (dev/test/prod)?&lt;/p&gt;&lt;p&gt;Below, we have hooked up variable groups to each stage (dev/test/prod) for our .Net Core apps logging settings.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/Library-Variables-and-Stages_B97A/sample-variable-group-to-stages_2.png"&gt;&lt;img width="785" height="316" title="sample-variable-group-to-stages" style="display: inline; background-image: none;" alt="sample-variable-group-to-stages" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/Library-Variables-and-Stages_B97A/sample-variable-group-to-stages_thumb[1].png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;So do you create three pipeline variables, one per stage in the pipeline variables?&amp;nbsp; Since the values are different and/or scoped to the stage, this seems like the logical choice and you can but it’s just adding clutter.&amp;nbsp; &lt;/p&gt;&lt;p&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/Library-Variables-and-Stages_B97A/variable-group-pipelinevar-reference-per-stage_2.png"&gt;&lt;img width="790" height="116" title="variable-group-pipelinevar-reference-per-stage" style="display: inline; background-image: none;" alt="variable-group-pipelinevar-reference-per-stage" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/Library-Variables-and-Stages_B97A/variable-group-pipelinevar-reference-per-stage_thumb[1].png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;You can define them once and use Release for the scope and the variable groups use the scoped value per the stage during the release to each stage*.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/Library-Variables-and-Stages_B97A/variable-group-pipelinevar-reference-to-release_2.png"&gt;&lt;img width="792" height="76" title="variable-group-pipelinevar-reference-to-release" style="display: inline; background-image: none;" alt="variable-group-pipelinevar-reference-to-release" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/Library-Variables-and-Stages_B97A/variable-group-pipelinevar-reference-to-release_thumb[1].png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;font size="4"&gt;* &lt;/font&gt;I know someone reading this is saying BUT BUT if the variable name (Logging.LogLevel.Default) in the variable group matches with what is in your settings file you don’t have to reference it at all in pipeline variables.&amp;nbsp; Correct.&amp;nbsp; However, there are times you might have to due to a mismatch or as we have by convention chosen to.&amp;nbsp; You&amp;nbsp; might notice we start our variable reference with a caret ^.&amp;nbsp; This is part of the convention we have adopted. &lt;a href="http://www.kevinlabranche.com/blog/conventions-for-using-variables-groups-in-azure-devops"&gt;More on that in a later post&lt;/a&gt;.&amp;nbsp; &lt;/p&gt;&lt;h4&gt;Overriding the Release scope&lt;/h4&gt;&lt;p&gt;Now, what if you wanted to override a value for a stage that has a Release based scope?&lt;/p&gt;&lt;p&gt;Add the new pipeline variable, set the stage and set the value to override with.&amp;nbsp; The variable group value for the stage will be overridden with your new value.&amp;nbsp; This is a great option when you are deviating from your convention or need to have a different setting for a while.&amp;nbsp;&amp;nbsp; It’s easy to cleanup.&amp;nbsp; Simply delete the pipeline variable for the stage when done.&amp;nbsp; We use this technique for a 3rd party vendor solution connection string that changes during their upgrade cycles.&amp;nbsp; During the upgrade timeline we override then go back to normal when the upgrade cycle completes.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/Library-Variables-and-Stages_B97A/override-variable-group-release-value_2.png"&gt;&lt;img width="785" height="66" title="override-variable-group-release-value" style="display: inline; background-image: none;" alt="override-variable-group-release-value" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/Library-Variables-and-Stages_B97A/override-variable-group-release-value_thumb[1].png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;You can also mark the Release scope variable as “settable at release time” instead of creating a stage variable override like shown above.&amp;nbsp; It is “faster” and if one was doing it for quick troubleshooting, no problem.&amp;nbsp;&amp;nbsp; For deviating from the normal/convention or prolonged usage I prefer an overridden stage variable.&amp;nbsp; The extra information provided by this tells anyone looking at the variables this isn’t “normal”.&amp;nbsp; It’s also less obtuse than having to view the logs of a release to see what the setting was set to at release time when using the “settable at release time” option.&amp;nbsp;&amp;nbsp; &lt;/p&gt;&lt;p&gt;Never, mix the two approaches for the same variable.&amp;nbsp; The overridden stage variable will win which might feel counterintuitive.&amp;nbsp; I’ll explain more on our conventions around variable groups in a later post.&lt;/p&gt;&lt;h4&gt;Learn More&lt;/h4&gt;&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/release/variables?view=azure-devops&amp;amp;tabs=batch#custom-variables"&gt;Custom Variables and Scope&lt;/a&gt;&lt;br&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/library/variable-groups?view=azure-devops&amp;amp;tabs=yaml"&gt;Variable Groups (Library)&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><category>Azure DevOps</category><category>deploy</category><category>pipelines</category><category>releases</category></item><item><trackback:ping>http://www.kevinlabranche.com/blog/feed/trackback/c196e7c6-b808-4026-8d45-f8d200c8a4b0</trackback:ping><pingback:server>http://www.kevinlabranche.com/blog/feed/pingback</pingback:server><pingback:target>http://www.kevinlabranche.com/blog/post/c196e7c6-b808-4026-8d45-f8d200c8a4b0</pingback:target><dc:creator>Kevin LaBranche</dc:creator><title>Managing a Windows File Share as part of your Azure DevOps Release</title><guid isPermaLink="false">http://www.kevinlabranche.com/blog/post/c196e7c6-b808-4026-8d45-f8d200c8a4b0</guid><link>http://www.kevinlabranche.com/blog/managing-a-windows-file-share-as-part-of-your-azure-devops-release</link><pubDate>Thu, 25 May 2023 19:49:10 GMT</pubDate><description>&lt;div&gt;&lt;p&gt;It’s important to have everything possible defined in your release or in an Infrastructure As Code (IAC) process.&amp;nbsp;&amp;nbsp; The holy grail is &lt;a href="https://www.hashicorp.com/resources/what-is-mutable-vs-immutable-infrastructure"&gt;immutable infrastructure&lt;/a&gt; but that isn’t always possible and maybe not worth trying to achieve. We still have many on premise IIS virtual machines that host several applications and the IAC setup handles the basic setup and configuration of IIS and the Windows environment itself only.&amp;nbsp; Nothing for the applications.&lt;br&gt;&lt;br&gt;In this situation, we have tried to setup our releases to be Application Infrastructure As Code (AIAC). If we have to move to another IIS environment we can change our deployment group and/or environment and deploy.&amp;nbsp; We get the added benefit of the release serving as further documentation to the requirements for the application that often go undocumented and makes for lift and shifts an exercise in frustration and rediscovery.&amp;nbsp; I call that application archeology.&amp;nbsp; Really not a position anyone wants to be in.&lt;/p&gt;&lt;p&gt;In an earlier post I described &lt;a href="http://www.kevinlabranche.com/blog/adding-a-virtual-directory-with-your-iis-web-application-manage-task-in-azure-devops"&gt;how one could add a separate virtual directory as part of the release&lt;/a&gt;.&amp;nbsp; A few of our applications also have file shares including a case where a virtual directory is also an upload folder for staff to include some very large files for the application to use.&amp;nbsp; Let’s get that into the release.&lt;/p&gt;&lt;p&gt;A PowerShell task is well suited to accomplish this.&amp;nbsp; I’ve included links in the Learn More section that goes over the nuances of file share rights and setup with PowerShell.&lt;/p&gt;&lt;p&gt;Create a PowerShell task that is after the task that creates the folder you want to share.&amp;nbsp; That’s about the only prerequisite.&amp;nbsp; It can go anywhere in the task step order otherwise.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/14a25a2c390a_7E60/share-management-ps-task_2.png"&gt;&lt;img width="440" height="346" title="share-management-ps-task" style="display: inline; background-image: none;" alt="share-management-ps-task" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/14a25a2c390a_7E60/share-management-ps-task_thumb.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;We chose to use the inline type with the below code:&lt;/p&gt;&lt;code&gt;$shareExists = Get-SmbShare -Name upload -ErrorAction Ignore
&lt;p&gt;if($shareExists -eq $null)&lt;br&gt;
{&lt;br&gt;&amp;nbsp;&amp;nbsp; New-SmbShare -Name upload -Description "Upload For ABC App" -Path c:\somefolderpath&lt;br&gt;&amp;nbsp;&amp;nbsp; Grant-SmbShareAccess -Name upload -AccountName domain\adgroup1 -AccessRight Change -Force&lt;br&gt;&amp;nbsp;&amp;nbsp; Grant-SmbShareAccess -Name upload -AccountName domain\adgroup2 -AccessRight Change -Force&lt;br&gt;&amp;nbsp;&amp;nbsp; Revoke-SmbShareAccess -Name upload -AccountName Everyone -Force&lt;br&gt;
}&lt;br&gt;
else&lt;br&gt;
{&lt;br&gt;&amp;nbsp;&amp;nbsp; Set-SmbShare -Name upload -Description "Upload For ABC App" -Force&lt;br&gt;&amp;nbsp;&amp;nbsp; Grant-SmbShareAccess -Name upload -AccountName domain\adgroup1 -AccessRight Change -Force&lt;br&gt;&amp;nbsp;&amp;nbsp; Grant-SmbShareAccess -Name upload -AccountName domain\adgroup2 -AccessRight Change -Force&lt;br&gt;&amp;nbsp;&amp;nbsp; Revoke-SmbShareAccess -Name upload -AccountName Everyone -Force&lt;br&gt;
}&lt;/p&gt;
&lt;/code&gt;&lt;p&gt;&lt;code&gt;$ACL = Get-ACL -Path "c:\somefolderpath"&lt;br&gt;
$AccessRuleOwner = New-Object System.Security.AccessControl.FileSystemAccessRule("domain\adgroup1","FullControl","ContainerInherit,ObjectInherit","None","Allow")&lt;br&gt;
$AccessRuleStaff = New-Object System.Security.AccessControl.FileSystemAccessRule("domain\adgroup2","Modify","ContainerInherit,ObjectInherit","InheritOnly","Allow")&lt;br&gt;
$ACL.SetAccessRule($AccessRuleOwner)&lt;br&gt;
$ACL.SetAccessRule($AccessRuleStaff)&lt;br&gt;
$ACL | Set-Acl -Path "c:\somefolderpath"&lt;/code&gt;&lt;p&gt;First, we try to get the share and if it doesn’t exist we set it up (New-SmbShare) and then setup the rights to the share, not the underlying folder/files.&amp;nbsp; If it does exist, we update the share.&amp;nbsp; Then we setup the folder/files in the underlying path for the share’s access rights.&amp;nbsp; We have an “owner” we setup with full rights, say our team member’s group and then a second group that represents the users and carefully set them up with change rights to everything below the root path but not the root itself.&lt;/p&gt;&lt;p&gt;Share access rights and folder/file access rights are different and the most restrictive rights will win. &lt;/p&gt;&lt;p&gt;Share access for the adgroup2 will have contribute since they can’t modify the root path but adgroup1 has read/write since they can as we granted them full rights to the root and below.&lt;/p&gt;&lt;p&gt;Share rights&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/14a25a2c390a_7E60/share-rights_4.png"&gt;&lt;img width="399" height="295" title="share-rights" style="display: inline; background-image: none;" alt="share-rights" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/14a25a2c390a_7E60/share-rights_thumb_1.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Folder/File rights:&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/14a25a2c390a_7E60/share-folder-perms_4.png"&gt;&lt;img width="447" height="61" title="share-folder-perms" style="display: inline; background-image: none;" alt="share-folder-perms" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/14a25a2c390a_7E60/share-folder-perms_thumb_1.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The PowerShell could be improved upon.&amp;nbsp; Instead of forcing an update we could interrogate the settings and only update if it’s changed.&amp;nbsp; Additionally, what if we wanted to use a new folder path or share name?&amp;nbsp; We’d want to add some concept of old/new and clean up after ourselves.&amp;nbsp; We didn’t do either in our case since these are long established paths and shares.&amp;nbsp; We don’t plan on changing them anytime soon and if we did the existing release is now clearly documented making it easy to amend for such a change.&lt;/p&gt;&lt;h4&gt;Learn More&lt;/h4&gt;&lt;p&gt;&lt;a href="https://4sysops.com/archives/managing-windows-file-shares-with-powershell/"&gt;Managing Windows file shares with PowerShell&lt;/a&gt;&lt;br&gt;&lt;a href="https://adamtheautomator.com/ntfs-permissions/"&gt;How To Manage NTFS Permissions With PowerShell&lt;/a&gt;&lt;br&gt;&lt;a href="https://blog.netwrix.com/2018/05/03/differences-between-share-and-ntfs-permissions/"&gt;Differences Between Share and NTFS Permissions&lt;/a&gt;&lt;br&gt;&lt;a href="https://serverfault.com/questions/259318/ntfs-acls-what-is-the-difference-between-object-and-container-inhertiance"&gt;NTFS ACL's: What is the difference between object and container inheritance?&lt;/a&gt;&lt;br&gt;&lt;a href="https://learn.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/ms229747(v=vs.100)?redirectedfrom=MSDN"&gt;ACL Propagation Rules&lt;/a&gt;&lt;br&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/api/system.security.accesscontrol.propagationflags?view=net-7.0"&gt;PropagationFlags Enum&lt;/a&gt;&lt;br&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/api/system.security.accesscontrol.inheritanceflags?view=net-7.0"&gt;InheritanceFlags Enum&lt;/a&gt;&lt;br&gt;&lt;a href="https://stackoverflow.com/questions/3282656/setting-inheritance-and-propagation-flags-with-set-acl-and-powershell"&gt;Setting Inheritance and Propagation flags with set-acl and powershell&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><category>Azure DevOps</category><category>deploy</category><category>iis</category><category>PowerShell</category></item><item><trackback:ping>http://www.kevinlabranche.com/blog/feed/trackback/1dfdc3e1-7ad2-48be-851f-481ec4523f4b</trackback:ping><pingback:server>http://www.kevinlabranche.com/blog/feed/pingback</pingback:server><pingback:target>http://www.kevinlabranche.com/blog/post/1dfdc3e1-7ad2-48be-851f-481ec4523f4b</pingback:target><dc:creator>Kevin LaBranche</dc:creator><title>Adding a virtual directory with your IIS web application manage task in Azure DevOps</title><guid isPermaLink="false">http://www.kevinlabranche.com/blog/post/1dfdc3e1-7ad2-48be-851f-481ec4523f4b</guid><link>http://www.kevinlabranche.com/blog/adding-a-virtual-directory-with-your-iis-web-application-manage-task-in-azure-devops</link><pubDate>Fri, 19 May 2023 05:17:00 GMT</pubDate><description>&lt;div&gt;&lt;p&gt;Using the&amp;nbsp; IIS web app manage task in Azure DevOps offers an easy and idempotent way of defining your web application on an IIS server.&amp;nbsp; You can think of it as Application Infrastructure As Code.&amp;nbsp; This task defines how the application should be setup/configured on an existing IIS server.&amp;nbsp; It allows for a deployment to a new IIS server to seamlessly work.&amp;nbsp; &lt;/p&gt;&lt;p&gt;What if you need to add a virtual directory as part of this setup?&amp;nbsp; An upload folder for example located outside the web applications virtual directory.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;Let’s take a look at three options, using the “Additional appcmd.exe commands” in the IIS web app manage task itself, using a second IIS web app manage task or a PowerShell task.&lt;/p&gt;&lt;p&gt;Of these options the “Additional appcmd.exe commands” immediately seems the way to go since it’s part of the setup task for the web app.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; – Use a second IIS web app manage task for most cases.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h4&gt;Using the “Additional appcmd.exe commands” in the IIS web app manage task itself&lt;/h4&gt;&lt;p&gt;I really want to like this option.&amp;nbsp; However, this option is too limiting to work for a scenario to add or change the virtual.&amp;nbsp; The IIS web app manage task itself is idempotent*.&amp;nbsp; It will add if an object doesn’t exist, change it if it does. * (maybe not fully idempotent…)&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/Adding-a-virtual-directory_A5CE/additional-appcmd_2.png"&gt;&lt;img width="714" height="174" title="additional-appcmd" style="display: inline; background-image: none;" alt="additional-appcmd" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/Adding-a-virtual-directory_A5CE/additional-appcmd_thumb.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;A naïve appcmd to use:&lt;/p&gt;&lt;code&gt;add vdir /app.name:”mywebsitename/mywebapp” /path:”/content” /physicalpath:"c:\somefolderpath”&lt;br&gt;&lt;br&gt;&lt;/code&gt;&lt;p&gt;This will work the first time but subsequent deploys will fail with duplicate collection element “/content”.&amp;nbsp; &lt;p&gt;You could change it to be a set statement but that will fail if the virtual directory doesn’t exist.&lt;/p&gt;&lt;p&gt;The text area only allows for appcmd commands but these commands can be piped together and the appcmd has the /in parameter to pass items to the next command in the pipe.&amp;nbsp; Since I’m trying to setup the release to be idempotent as much as possible this is looking a bit grim. &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h5&gt;What about Pipes and /in for appcmd?&lt;/h5&gt;&lt;p&gt;So, let’s see if the virtual exists and set it if it does.&lt;/p&gt;&lt;code&gt;list vdir /physicalpath:"c:\somefolderpath" /xml | set vdir /in /physicalpath:"c:\somefolderpath" /path:"/content"&lt;/code&gt;&lt;p&gt;&lt;br&gt;Great, that works.&amp;nbsp; However, what if the virtual doesn’t exist?&amp;nbsp; This could be my own failing but it doesn’t appear possible to pipe this out in a way to add.&amp;nbsp; It also doesn’t appear possible to if/else to do a set or an add with pipes and the appcmd.&amp;nbsp; &lt;/p&gt;&lt;p&gt;What about piping to other functions? How about list piped to find with an or to add for the not found case.&lt;/p&gt;&lt;code&gt;list vdir /physicalPath:"c:\somefolderpath" 
| findstr "somefolderpath" || add vdir /app.name:"mywebsitename/mywebapp" /path:"/content" /physicalpath:"c:\somefolderpath"&lt;/code&gt;&lt;p&gt;&lt;br&gt;You’ll get errors about invalid tokens.&amp;nbsp; You can only use appcmd commands in this text area.&lt;/p&gt;&lt;p&gt;While one can pipe appcmd’s together and use the /in parameter for input to the next pipe, the inability (or at least my inability) to change the appcmd from an add if the virtual doesn’t exist to a set if it does makes this a no go.&amp;nbsp; &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h5&gt;What about Continue On Error and Run this task Even if a previous task failed combination?&lt;/h5&gt;&lt;p&gt;Well, one could include add and set as two different lines in the text area and set Continue On Error for the task and a downstream task to run Even if a previous task failed.&amp;nbsp; However, this has the downside of hiding real errors with this task.&amp;nbsp; Plus, I can’t stand seeing the partially succeeded icon.&amp;nbsp; It always make me think something is broken and must be addressed.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/Adding-a-virtual-directory_A5CE/PartiallySucceeded_2.png"&gt;&lt;img width="536" height="84" title="PartiallySucceeded" style="display: inline; background-image: none;" alt="PartiallySucceeded" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/Adding-a-virtual-directory_A5CE/PartiallySucceeded_thumb.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;I even tried to NUL out the output to swallow the error. &lt;/p&gt;&lt;code&gt;add vdir /app.name:”mywebsitename/mywebapp” /path:”/content” /physicalpath:"c:\somefolderpath” &amp;gt; NUL&lt;/code&gt;&lt;p&gt;&lt;br&gt;No go, errors. So, at the moment, I don’t see this as a viable option for adding or setting a virtual directory.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h4&gt;Using a second IIS web app manage task&lt;/h4&gt;&lt;p&gt;You can have more than one IIS web app manage task.&amp;nbsp; Let’s create a second one after the initial task to setup the web app.&lt;/p&gt;&lt;p&gt;On this task, select for the Configuration type, IIS Virtual Directory.&lt;/p&gt;&lt;p&gt;The parameters might be a bit confusing if you know you way around the appcmd but for the&amp;nbsp; parameters as used in above examples:&lt;/p&gt;&lt;pre&gt;Parent website name: mywebsitename&lt;p&gt;Virtual Path: /mywebapp/content&lt;/p&gt;&lt;p&gt;Physical Path: c:\somefolderpath&lt;/p&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/Adding-a-virtual-directory_A5CE/manage-iisvdir-task_2.png"&gt;&lt;img width="643" height="286" title="manage-iisvdir-task" style="display: inline; background-image: none;" alt="manage-iisvdir-task" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/Adding-a-virtual-directory_A5CE/manage-iisvdir-task_thumb.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Since the task is smart enough to add or change we have achieved the desired result quickly and easily.&amp;nbsp; &lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h5&gt;YAML example&lt;/h5&gt;&lt;p&gt;If you happen to be using YAML, below is the equivalent commands for the IIS Web App Manage Task:&lt;/p&gt;&lt;code&gt;- task: IISWebAppManagementOnMachineGroup@0&lt;br&gt;&amp;nbsp; displayName: 'Manage IISVirtualDirectory - Add or Set content virtual directory'&lt;br&gt;&amp;nbsp; inputs:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IISDeploymentType: IISVirtualDirectory&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ParentWebsiteNameForVD: 'mywebsitename'&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; VirtualPathForVD: '/mywebapp/content'&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PhysicalPathForVD: 'c:\somefolderpath'&lt;br&gt;&lt;br&gt;&lt;/code&gt;&lt;h4&gt;Using a PowerShell task&lt;/h4&gt;&lt;p&gt;I like PowerShell.&amp;nbsp; I’m not the best at it but I use it often for automation tasks around DevOps.&amp;nbsp; So how would we accomplish with a PowerShell task.&lt;/p&gt;&lt;p&gt;The code below was heavily inspired by the source for the&amp;nbsp; &lt;a href="https://github.com/microsoft/azure-pipelines-extensions/blob/master/Extensions/IISWebAppDeploy/Src/Tasks/IISWebAppMgmt/IISWebAppMgmtV3"&gt;IIS web app manage task&lt;/a&gt;.&lt;br&gt;&lt;/p&gt;&lt;code&gt;$vdirNameToFind="mywebsitename/mywebapp/content"
&lt;p&gt;c:\windows\system32\inetsrv\appcmd.exe list vdir /vdir.name:$vdirNameToFind&lt;/p&gt;
&lt;p&gt;$vdir=c:\windows\system32\inetsrv\appcmd.exe list vdir /vdir.name:$vdirNameToFind&lt;/p&gt;
&lt;/code&gt;&lt;p&gt;&lt;code&gt;if($vdir -ne $null -and $vdir -like "*`"$vdirNameToFind`"*") # like needed as vdir name can match parent vdirs&lt;br&gt;
{&lt;br&gt;&amp;nbsp;&amp;nbsp; Write-Host "virtual found, updating"&lt;br&gt;&amp;nbsp;&amp;nbsp; c:\windows\system32\inetsrv\appcmd.exe set vdir /vdir.name:$vdirNameToFind /path:"/content" /physicalPath:"c:\somefolderpath"&lt;br&gt;
}&lt;br&gt;
else &lt;br&gt;
{&lt;br&gt;&amp;nbsp;&amp;nbsp; Write-Host "virtual not found, adding"&lt;br&gt;&amp;nbsp;&amp;nbsp; c:\windows\system32\inetsrv\appcmd.exe add vdir /app.name:"mywebsitename/mywebapp" /path:"/content" /physicalPath:"c:\somefolderpath"&lt;br&gt;
}&lt;br&gt;&lt;/code&gt;&lt;p&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/Adding-a-virtual-directory_A5CE/powershell-vdir-task_2.png"&gt;&lt;img width="599" height="232" title="powershell-vdir-task" style="display: inline; background-image: none;" alt="powershell-vdir-task" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/Adding-a-virtual-directory_A5CE/powershell-vdir-task_thumb.png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The source from the IIS web app manage task has a far better flushed out solution so I recommend using the task but maybe one needs to do other work in the PowerShell task and having it all it scripted is desired.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h4&gt;Learn More&lt;/h4&gt;&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/reference/iisweb-app-management-on-machine-group-v0?view=azure-pipelines"&gt;IIS Web App Manage Task&lt;/a&gt; &lt;br&gt;&lt;a href="https://learn.microsoft.com/en-us/iis/get-started/getting-started-with-iis/getting-started-with-appcmdexe"&gt;Getting started with AppCmd&lt;/a&gt;&lt;br&gt;&lt;a href="https://blogs.iis.net/ksingla/things-you-can-do-by-piping-appcmd-commands"&gt;Piping AppCmd&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</description><category>Azure DevOps</category><category>deploy</category><category>iis</category><category>PowerShell</category></item><item><trackback:ping>http://www.kevinlabranche.com/blog/feed/trackback/a93ecd9a-78bf-477e-832e-5f5476095745</trackback:ping><pingback:server>http://www.kevinlabranche.com/blog/feed/pingback</pingback:server><pingback:target>http://www.kevinlabranche.com/blog/post/a93ecd9a-78bf-477e-832e-5f5476095745</pingback:target><dc:creator>Kevin LaBranche</dc:creator><title>Passing variables between PowerShell file tasks in classic Azure DevOps pipelines</title><guid isPermaLink="false">http://www.kevinlabranche.com/blog/post/a93ecd9a-78bf-477e-832e-5f5476095745</guid><link>http://www.kevinlabranche.com/blog/passing-variables-between-powershell-file-tasks-in-classic-azure-devops-pipelines</link><pubDate>Fri, 12 May 2023 23:03:32 GMT</pubDate><description>&lt;div&gt;&lt;p&gt;I ran into a bump when trying to pass variables around between PowerShell files in two tasks in the same job. I found the PowerShell task acts differently when using inline vs file path type. &lt;/p&gt;&lt;h4&gt;Inline vs File Path&lt;/h4&gt;&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/process/set-variables-scripts?view=azure-devops&amp;amp;tabs=powershell"&gt;Set variables in scripts - Azure Pipelines | Microsoft Learn&lt;/a&gt; shows how to pass variables around between inline tasks in the same job.&amp;nbsp; &lt;/p&gt;&lt;p&gt;set it: &lt;p&gt;&lt;code&gt;Write-Host "##vso[task.setvariable variable=myVar;]foo"&lt;/code&gt;&lt;p&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/b33af298a16b_B9DB/set-it-task-inline_2.png"&gt;&lt;img width="463" height="217" title="set-it-task-inline" style="display: inline; background-image: none;" alt="set-it-task-inline" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/b33af298a16b_B9DB/set-it-task-inline_thumb[1].png" border="0"&gt;&lt;/a&gt;&lt;p&gt;Subsequent inline tasks in the same job can use it:&amp;nbsp; &lt;code&gt;Write-Host "myVar is $(myVar)"&lt;/code&gt;&lt;p&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/b33af298a16b_B9DB/show-it-inline-task_2.png"&gt;&lt;img width="466" height="249" title="show-it-inline-task" style="display: inline; background-image: none;" alt="show-it-inline-task" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/b33af298a16b_B9DB/show-it-inline-task_thumb[1].png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;h4&gt;File Path Type&lt;/h4&gt;&lt;p&gt;Referencing variables in File Path Type PowerShell Tasks changes slightly.&amp;nbsp; If you try to access the variable &lt;code&gt;$(myVar)&lt;/code&gt; like the inline example you get an error:&amp;nbsp; &lt;pre style="white-space: pre-wrap;"&gt;myVar : The term 'myVar' is not recognized as the name of a cmdlet, function, script file, or operable program.&lt;/pre&gt;&lt;p&gt;There are two ways to accomplish this with the File Path Type.&lt;/p&gt;&lt;h6&gt;Environment Variable&lt;/h6&gt;&lt;p&gt;Use the environment ($env):&lt;/p&gt;&lt;code&gt;$env:myVar&lt;/code&gt;&lt;h6&gt;Output Variable&lt;/h6&gt;&lt;p&gt;Use an output variable and pass it as a parameter into the next script task.&amp;nbsp; This also works for tasks not in the same job.&lt;/p&gt;&lt;p&gt;The syntax to set the variable in the Set it step’s script file changes to:&lt;/p&gt;&lt;p&gt;&lt;code&gt;Write-Host "##vso[task.setvariable variable=myVar;isoutput=true]foo"&lt;/code&gt;&lt;p&gt;In the task, set the Reference name in the task to a name of your choosing.&amp;nbsp; I used setOutput.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/b33af298a16b_B9DB/set-it-task_2.png"&gt;&lt;img width="480" height="275" title="set-it-task" style="display: inline; background-image: none;" alt="set-it-task" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/b33af298a16b_B9DB/set-it-task_thumb[1].png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The consuming task, uses an argument in the consuming PowerShell file and uses the Reference Name from the previous task. If you called the argument in the PowerShell file &lt;code&gt;$myVarArg&lt;/code&gt; then you pass it in &lt;code&gt;–myVarArg “$(setOutput.myVar)”&lt;/code&gt;&lt;p&gt;&lt;a href="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/b33af298a16b_B9DB/showit-task_2.png"&gt;&lt;img width="485" height="229" title="showit-task" style="display: inline; background-image: none;" alt="showit-task" src="http://www.kevinlabranche.com/blog/content/binary/Open-Live-Writer/b33af298a16b_B9DB/showit-task_thumb[1].png" border="0"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The PowerShell file needs to accept that parameter:&lt;/p&gt;&lt;p&gt;&lt;code&gt;Param(&lt;/code&gt;&lt;p&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; [string]$myVarArg&lt;/code&gt;&lt;p&gt;&lt;code&gt;)&lt;/code&gt;&lt;p&gt;&lt;code&gt;Write-Host "myVar: $myVarArg"&lt;/code&gt;&lt;/p&gt;&lt;p&gt;See &lt;a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/release/variables?view=azure-devops&amp;amp;tabs=powershell#use-custom-variables"&gt;Classic release and artifacts variables - Azure Pipelines | Microsoft Learn&lt;/a&gt; for more information.&lt;/p&gt;&lt;p&gt;The advantage of the second option is that it also works between jobs.&amp;nbsp; If you are using the variables in the same job, consider using the environment variable.&lt;/p&gt;&lt;/div&gt;</description><category>Azure DevOps</category><category>PowerShell</category></item></channel></rss>