<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: mieel</title>
    <description>The latest articles on DEV Community by mieel (@mieel).</description>
    <link>https://dev.to/mieel</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F357610%2Fe9900254-8eca-4016-9aaa-f109bff69dd9.png</url>
      <title>DEV Community: mieel</title>
      <link>https://dev.to/mieel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mieel"/>
    <language>en</language>
    <item>
      <title>Automate Azure AD User Provisioning, with auto-generated passwords in Azure Keyvault</title>
      <dc:creator>mieel</dc:creator>
      <pubDate>Mon, 27 Apr 2020 15:03:52 +0000</pubDate>
      <link>https://dev.to/mieel/automate-azure-ad-user-provisioning-with-auto-generated-passwords-in-azure-keyvault-4pie</link>
      <guid>https://dev.to/mieel/automate-azure-ad-user-provisioning-with-auto-generated-passwords-in-azure-keyvault-4pie</guid>
      <description>&lt;h1&gt;
  
  
  Use Case
&lt;/h1&gt;

&lt;p&gt;Ever encountered a situation where you are about deploy an App in a Windows Environment, and you realize you don't have an designated user account yet?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You would ask your AD admin to create one for you (or do it yourself).&lt;/li&gt;
&lt;li&gt;Figure out a password with the correct complexity&lt;/li&gt;
&lt;li&gt;Note down the password (securely 😉) so that Team members can use it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I assembled a script that takes a list of &lt;strong&gt;Users&lt;/strong&gt; and a set of &lt;strong&gt;Environments&lt;/strong&gt; (&lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;, &lt;code&gt;prod&lt;/code&gt;, ...), which then either creates or updates the &lt;strong&gt;AAD User&lt;/strong&gt;.&lt;br&gt;
Passwords are auto-generated and backed in an &lt;strong&gt;Azure Keyvault&lt;/strong&gt; -- this makes noting down passwords a thing of the past 🎉. &lt;br&gt;
You can find the boilerplate script (or set of functions) &lt;a href="https://github.com/mieel/snippets/blob/master/powershell/Ensure-AzureADUserAccounts.ps1"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This is meant to tailor to your business logic.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;
  
  
  Explanation
&lt;/h1&gt;

&lt;p&gt;In my example, I have a simple list of &lt;strong&gt;Services&lt;/strong&gt; and &lt;strong&gt;Environments&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$Services = @(
@{ name = 'service_1' ; description = 'Service 1'},
....
)
$envs  =  @('Dev','Test','Acc','Stage','Prod')
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The User Account Names are consctructed from the &lt;strong&gt;Service&lt;/strong&gt; and &lt;strong&gt;Environment&lt;/strong&gt;, so for example the account name for &lt;code&gt;service_1&lt;/code&gt; in &lt;code&gt;development&lt;/code&gt; would be: &lt;code&gt;svc_d_service_1&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: Again, Account Name logic can be entirely different in your business&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For each &lt;strong&gt;User Account&lt;/strong&gt;, the main Function &lt;code&gt;Set-ServiceAccountUser&lt;/code&gt; is called:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it checks if the User exists&lt;/li&gt;
&lt;li&gt;if it doesnt exist, the User will be created.&lt;/li&gt;
&lt;li&gt;When an User needs to be created, it checks the &lt;strong&gt;Azure Keyvault&lt;/strong&gt; whether it has a Password available, if not, a Password is auto-generated  and stored in the Vault.&lt;/li&gt;
&lt;li&gt;If the User already exists, you can optionally update the password (in the Keyvault and AD) with the &lt;code&gt;-updatePassword&lt;/code&gt; switch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Passwords are generated in the form of a &lt;code&gt;wer-yui-wwer35&lt;/code&gt; (three syllables plus 2 digits).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: Passwords logic can be found in the &lt;code&gt;New-Password&lt;/code&gt; function. I'm using the &lt;code&gt;pronounceable&lt;/code&gt; pattern of the &lt;code&gt;https://makemeapassword.ligos.net&lt;/code&gt; api. This is totally customizable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You will also notice that I use different Keyvaults for each &lt;strong&gt;Environment&lt;/strong&gt;. You can find (and customize) the logic in the &lt;code&gt;Get-VaultName&lt;/code&gt; Function.&lt;/p&gt;

</description>
      <category>powershell</category>
      <category>azure</category>
      <category>activedirectory</category>
      <category>windows</category>
    </item>
    <item>
      <title>How to Distribute Secrets for PowerShell Scripts Using Ansible</title>
      <dc:creator>mieel</dc:creator>
      <pubDate>Fri, 17 Apr 2020 05:47:40 +0000</pubDate>
      <link>https://dev.to/mieel/how-to-distribute-secrets-for-powershell-scripts-using-ansible-3mne</link>
      <guid>https://dev.to/mieel/how-to-distribute-secrets-for-powershell-scripts-using-ansible-3mne</guid>
      <description>&lt;h1&gt;
  
  
  The Use Case
&lt;/h1&gt;

&lt;p&gt;Managing secrets is hard. Everything needs to run under its own username/password, and apparently keeping plaintext passwords in scripts is really bad. &lt;/p&gt;

&lt;p&gt;To manage secrets better at our company we already implemented the following practices:&lt;br&gt;
1) Avoiding hardcoding credentials (or any other configuration data) in the scripts, and commit scripts and configs separately.&lt;/p&gt;

&lt;p&gt;2) Not actually storing real values in the config files: Instead, use placeholders like &lt;code&gt;#{sqlserverPassword}#&lt;/code&gt; as values, and use a &lt;strong&gt;CI Task&lt;/strong&gt; to replace the tokens with the actual values when deploying the script (many CI platforms have this feature out-of-the-box).&lt;/p&gt;

&lt;p&gt;So even though we don't store passwords in Source Control anymore, when the scripts are deployed, the files may still contain passwords in plaintext.&lt;/p&gt;

&lt;p&gt;You could use &lt;code&gt;Integrated Security&lt;/code&gt; to avoid using passwords altogether, but this is only an option if your workers and resources are in the same &lt;strong&gt;Windows Domain&lt;/strong&gt;. &lt;br&gt;
You can generate &lt;code&gt;SecureStrings&lt;/code&gt; and have your script users use those instead. It's pretty secure because only users can decrypt the &lt;code&gt;SecureStrings&lt;/code&gt; that the same user had created on the same machine. But the downside is only users can decrypt the &lt;code&gt;SecureStrings&lt;/code&gt; that the same user had created on the same machine 😅&lt;/p&gt;

&lt;p&gt;So how to solve this?&lt;/p&gt;

&lt;p&gt;First, we need a method for adding and retrieving secrets: Use Microsofts new &lt;a href="https://devblogs.microsoft.com/powershell/secrets-management-development-release/"&gt;SecretsManagement Module&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The Module is still in PreRelease, but it can Add Secrets and Get Secrets, so good enough for our use case for now&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So what is this Module?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Secrets Management module helps users manage secrets by providing a set of cmdlets that let you store secrets locally, using a local vault provider, and access secrets from remote vaults. This module supports an extensible model where local and remote vaults can be registered and unregistered on the local machine, per user, for use in accessing and retrieving secrets.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notice that it says &lt;em&gt;per user&lt;/em&gt;: Users can only access their own local vault (assuming we are not using external vaults like &lt;strong&gt;Azure KeyVault&lt;/strong&gt;, which probably would make this whole tutorial unnessecary). &lt;br&gt;
To have the password available for the user to use, we must perform an &lt;code&gt;Add-Secret&lt;/code&gt; command under the  user context, so that the user can do &lt;code&gt;Get-Secret&lt;/code&gt; to retrieve the password stored in their own vault. &lt;br&gt;
The workflow would be something like: Invoke a command with the User Credentials passed as &lt;code&gt;-Credential&lt;/code&gt;, executing a Scriptblock &lt;code&gt;{ Add-Secret -Name MySecret -Secret SuperSecretPassword }&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;🚦 &lt;em&gt;If there is a better/different way to provide secrets under different user contexts, please let me know.&lt;br&gt;
For now, we continue this route.&lt;/em&gt; 🚦&lt;/p&gt;
&lt;h1&gt;
  
  
  The PowerShell Script to Add Secrets
&lt;/h1&gt;

&lt;p&gt;I tried many methods to perform commands under a different user context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use &lt;code&gt;Invoke-Command -ScriptBlock $Scriptblock -Credential $creds&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;why not? This requires &lt;strong&gt;WinRM&lt;/strong&gt; to be available for the given user, even if the command is run on &lt;code&gt;localhost&lt;/code&gt;, so I looked for something else.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;Start-Job -ScriptBlock $Scriptblock -Credential $creds | Wait-Job | Receive-Job&lt;/code&gt; Combo

&lt;ul&gt;
&lt;li&gt;why not? This worked pretty consistently, up until to point I tried to integrate this in a CI Task. I got stuck getting ❌&lt;code&gt;2100,PSSessionStateBroken&lt;/code&gt; errors, and it seems to be a &lt;a href="https://issues.jenkins-ci.org/browse/JENKINS-49159"&gt;common issue&lt;/a&gt; in CI systems. Apparently it has something to do with credentials not being passed while 'double hopping', resolving this would be again setting up WinRM in conjunction with something called CrepSSP.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So finally I ended up using the &lt;a href="https://github.com/mkellerman/Invoke-CommandAs"&gt;Invoke-CommandAs&lt;/a&gt; Module, which under the hood creates a &lt;strong&gt;Scheduled Task&lt;/strong&gt; as the user, carries out your commands you specify in a &lt;code&gt;$ScriptBlock&lt;/code&gt;, and finally removes the Task.&lt;br&gt;
An example snippet for adding 1 secret for 1 user on 1 machine would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$userName = "domain\user_account"
$userPassword = "userPassword" # ✋Warning If the password contains $ signs, use single quotes!        
[pscredential]$credObject = New-Object System.Management.Automation.PSCredential ($userName,         $userPassword)            
$Credentials = Get-Credential $credObject 
$ScriptBlock = {          
   If ( -not(Get-Module Microsoft.PowerShell.SecretsManagement -listAvailable) ) {
      Write-Host "Secret Module not found, installing.."
      [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
      Install-Module -Name Microsoft.PowerShell.SecretsManagement -RequiredVersion 0.2.0-alpha1 -AllowPrerelease -Repository psgallery -Force -Scope CurrentUser
   }
   # proof that it worked:
   Write-Host $env:computername
   Write-Host $env:username
   Get-SecretInfo
}
Invoke-CommandAs -Scriptblock $ScriptBlock -AsUser $Credentials
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Notice the Tls Securtity protocol, I almost flipped my desk when I couldn't figure out why the Module wouldn't download, until I saw &lt;a href="https://devblogs.microsoft.com/powershell/powershell-gallery-tls-support/"&gt;this&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: The user needs the right amount of user rights permissions to pull this off. I haven't fully figured out the exact set of permissions, I thought the user only needs the local &lt;code&gt;log on as a batch job&lt;/code&gt; permission, but couldn't get this to work consistenly. &lt;br&gt;
 &lt;strong&gt;Solution&lt;/strong&gt;: So I decided to throw this hack in for now: I ended up temporaly adding the user to the local &lt;code&gt;Administrators&lt;/code&gt; group, add the secrets, and then remove it again from the group.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NET LOCALGROUP "Administrators" $userName /ADD | Out-Null
Invoke-CommandAs -Scriptblock $ScriptBlock -AsUser $Credentials
NET LOCALGROUP "Administrators" $userName /remove| Out-Null
##📣 If anyone has a better idea, please let me know :halp: 📣
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;🎉 Great, now that we have cobbled together a working script, we can now use this for every secret, for each user, on every machine that the user operates on, right? Also, apparantly current security conventions suggest that we should run each service as a different user, therefore increasing the amount users we have to manage 🤯. You might see how this can be a bit tedious to manage manually.&lt;br&gt;
But here comes &lt;strong&gt;Ansible&lt;/strong&gt;.&lt;/p&gt;
&lt;h1&gt;
  
  
  The  Ansible Playbook
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Ansible is a radically simple IT automation engine that automates &lt;a href="https://www.ansible.com/provisioning?hsLang=en-us"&gt;cloud provisioning&lt;/a&gt;, &lt;a href="https://www.ansible.com/configuration-management?hsLang=en-us"&gt;configuration management&lt;/a&gt;, &lt;a href="https://www.ansible.com/application-deployment?hsLang=en-us"&gt;application deployment&lt;/a&gt;, &lt;a href="https://www.ansible.com/orchestration?hsLang=en-us"&gt;intra-service orchestration&lt;/a&gt;, and many other IT needs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you haven't played around with Ansible yet, I suggest watching the &lt;a href="https://www.youtube.com/watch?v=goclfp6a2IQ&amp;amp;list=PL2_OBreMn7FplshFCWYlaN2uS8et9RjNG&amp;amp;index=4"&gt;live-streams&lt;/a&gt; of this Jeff Geerling guy or check out his &lt;a href="https://leanpub.com/ansible-for-devops"&gt;Ansible book&lt;/a&gt; (which is currently free)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: If there are more 'ansible' ways to achieve this use-case, please let me know.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The highlevel workflow would be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;hosts: list of servers&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;variables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;accounts&lt;/code&gt;: 
a dictionary where key = accountname, value = password&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;account_mapped_secrets&lt;/code&gt;:
a dictionary where  key = accountname, value= list of secret-keys&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;secrets&lt;/code&gt;:
a dictionary where key = secret-key, value=secret-value&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;playbook:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get list of &lt;code&gt;accounts&lt;/code&gt; that have secrets mapped&lt;/li&gt;
&lt;li&gt;Loop over  &lt;code&gt;accounts&lt;/code&gt;, and for each &lt;code&gt;account&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;construct a secret key/value disctionary for each &lt;code&gt;account_secret&lt;/code&gt; of the &lt;code&gt;account&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;use the above &lt;code&gt;Invoke-CommandAs&lt;/code&gt; powershell snippet under the &lt;code&gt;account&lt;/code&gt; user context to add each secret-key/value&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A play would look something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;main.yml&lt;/code&gt; file
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: Loop over Accounts that has Secrets to deployed
  include_tasks: add_secrets.yml  
  with_items: "{{ account_mapped_secrets }}"  
  loop_control:  
    loop_var: account  
    extended: yes
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;the included tasks &lt;code&gt;add_secrets.yml&lt;/code&gt; file
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---  
- name: "${{ account }}$ -- Create temp Vault dict"  
  set_fact:  
    account_secrets_{{ ansible_loop.index }}: {}  

- name: "${{ account }}$ -- Populate Secrets"  
  set_fact:  
   account_secrets_{{ ansible_loop.index }}: "{{ lookup('vars', 'account_secrets_' ~ ansible_loop.index) |default({}) | combine( {item: secrets[item]} ) }}"  
  with_items: "{{ account_mapped_secrets[account] }}"  

- name: "${{ account }}$ -- Add Secrets to Local Vault"  
  win_shell: |  
   $userName = '{{ account }}'  
   $userPassword = '{{ accounts[account] }}'  
   [securestring]$secStringPassword = ConvertTo-SecureString $userPassword -AsPlainText -Force  
   [pscredential]$credObject = New-Object System.Management.Automation.PSCredential ($userName, $secStringPassword)  

   $ScriptBlock = {  

   Get-SecretInfo | Remove-Secret -Vault BuiltInLocalVault  #✋for demo purposes we delete any existing Secrets.
   $json = @"  
   {{ lookup('vars', 'account_secrets_' ~ ansible_loop.index) | to_nice_json }}  
   "@  
   ($json | ConvertFrom-Json).psobject.properties | ForEach-Object { Add-Secret -Name $_.Name -  Secret $_.Value }  
   Get-SecretInfo  

   }  
   try {  
     NET LOCALGROUP "Administrators" $userName /ADD | Out-Null  
     Invoke-CommandAs -ScriptBlock $ScriptBlock -AsUser $credObject -verbose  
     NET LOCALGROUP "Administrators" $userName /DELETE | Out-Null  
   } catch {  
     Write-Error $_  
   }  
  register: shellresult  

- name: "${{ account }}$ -- Fail if output is not exptected"  
  fail:  
    msg: shellresult.stdout_lines  
  when: shellresult.stdout.find("Vault") == -1  

- name: "${{ account }}$ -- Show Errors"  
  fail:  
    msg: "{{ shellresult.stderr }}"  
  when: shellresult.stderr != ""  

- name: "${{ account }}$ -- Print Result"  
  debug:  
    msg: "{{ shellresult.stdout_lines }}"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;✋ You need to setup an &lt;a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html"&gt;Ansible Role&lt;/a&gt; to make this snippet work as it is.&lt;br&gt;
My directory looks someting like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/etc/ansible  
├── ansible.cfg  
├── environs  
│ ├── dev                     # copy this subfolder for each environment (dev,test,prod...) you might have  
│ │ ├── group_vars  
│ │ │ └── all  
│ │ │      ├── main.yaml      # contains account_mapped_secrets variable
│ │ │      └── accounts.yaml  # contains accounts variable
| | |      └── secrets.yaml   # contains secrets variable
│ │ ├── hosts  
....
├── roles  
│ ├── secrets  
│ │ ├── tasks  
│ │ │ ├── add_secrets.yml     # the core of the play
│ │ │ └── main.yml            # the main tasks file that includes the above file in a loop
├── play_secrets_role.yaml    # a play that calls the secrets role
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To run this play for the &lt;code&gt;dev&lt;/code&gt; environment, run: &lt;code&gt;ansible-playbook -i environs/dev play_secrets_role.yaml&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Explaining the playbook:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You can notice that I'm construcing an unique dictionary in each loop &lt;code&gt;account_secrets_{{ ansible_loop.index }}&lt;/code&gt;. In the first versions of my playbook I just used &lt;code&gt;account_secrets&lt;/code&gt; as the variable, and apparantly in Ansible, once you set a fact, you can't unset it. So the secrets dictionary would just keep appending between each loop, and the last user would end up having all the secrets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ansible_loop.index&lt;/code&gt; is the current iteration in the loop, but is only available when you have the &lt;code&gt;extended: yes&lt;/code&gt; option when looping.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We use a jinja &lt;code&gt;combine()&lt;/code&gt; expression to dynamically create the &lt;code&gt;account_secrets_x&lt;/code&gt; dictionary. Note that I refer to the above variable with the &lt;code&gt;lookup('vars', 'account_secrets_' ~ ansible_loop.index)&lt;/code&gt; syntax instead of the &lt;code&gt;account_secrets_{{ ansible_loop.index }}&lt;/code&gt;. &lt;br&gt;
This is because the following won't work: &lt;code&gt;"{{ account_secrets_{{ ansible_loop.index }}) |defaul t({}) | combine( {item: secrets[item]} ) }}"&lt;/code&gt; as you can't have double interpolation inside a jinja expression.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wait a minute, you're still storing plaintext passwords in the variable files!
&lt;/h2&gt;

&lt;p&gt;Here comes Ansible again. By separate the &lt;code&gt;accounts&lt;/code&gt; and &lt;code&gt;secrets&lt;/code&gt; from the &lt;code&gt;account_mapped_secrets&lt;/code&gt; into different variable files we can encrypt the first two with &lt;a href="https://docs.ansible.com/ansible/latest/user_guide/vault.html#file-level-encryption"&gt;Ansible Vault&lt;/a&gt;&lt;br&gt;
Then when running your &lt;code&gt;ansible-playbook&lt;/code&gt;, pass in the extra argument &lt;code&gt;--ask-vault-pass&lt;/code&gt;&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>powershell</category>
      <category>windows</category>
    </item>
  </channel>
</rss>
