<?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>Forem: Jacco Kulman</title>
    <description>The latest articles on Forem by Jacco Kulman (@jacco).</description>
    <link>https://forem.com/jacco</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%2F864816%2F8d3490d4-cd83-4c19-af40-3d9d0b5a545d.jpeg</url>
      <title>Forem: Jacco Kulman</title>
      <link>https://forem.com/jacco</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jacco"/>
    <language>en</language>
    <item>
      <title>Safely scripting with boto3</title>
      <dc:creator>Jacco Kulman</dc:creator>
      <pubDate>Wed, 22 Nov 2023 13:14:28 +0000</pubDate>
      <link>https://forem.com/aws-builders/safely-scripting-with-boto3-3cb0</link>
      <guid>https://forem.com/aws-builders/safely-scripting-with-boto3-3cb0</guid>
      <description>&lt;p&gt;It is very common to set up the AWS credentials used by &lt;code&gt;boto3&lt;/code&gt; and the &lt;code&gt;aws&lt;/code&gt; client in &lt;code&gt;~/.aws/config&lt;/code&gt; and &lt;code&gt;~/.aws/credentials&lt;/code&gt;. There are some security risks you need to be aware of though. In this article I will explain risks and mitigation when scripting against your AWS account(s).&lt;/p&gt;

&lt;p&gt;The risks involved in working this way are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;long-term credentials eventually leak&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the accidental running destructive code&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;generic roles are overly permissive for specific task&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;scripting is not infrastructure as code&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;without mfa this is still single factor auth&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will go into each of these risks below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compromised credentials
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7mV4izOJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tj2zn2rx3rejfbffbaj7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7mV4izOJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tj2zn2rx3rejfbffbaj7.png" alt="i know: it doesn't rhyme" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The credentials often used in this scenario are valid indefinitely. The longer you keep a secret unchanged, the higher the chance of it being compromised. Lately Center for Internet Security (CIS) lowered the required minimum rotation frequency of credentials and secrets from 90 to 45 days in its AWS benchmarks! If you do not rotate the credentials periodically yourself, they will be working as long as your account exists (put a periodic reminder in your calendar!). If you left your laptop unlocked unattended for just a one minute, you should consider the contents of your &lt;code&gt;~/.aws/credentials&lt;/code&gt; compromised, especially if you hang out in places with many cloud engineers 😉.&lt;/p&gt;

&lt;p&gt;It is much safer to &lt;em&gt;not&lt;/em&gt; use long-term credentials at all. Instead use the short-term credentials you can can on the sso-pages once you set that up:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5DwfeyK---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ezmmdv618chiegq5xbrd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5DwfeyK---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ezmmdv618chiegq5xbrd.png" alt="sso credentials" width="800" height="545"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can just click the credential exports and paste them in the console you are using to execute the script. These credentials are valid for a limited time and they are only known to the console you paste them in. This is equivalent to using &lt;code&gt;get_session_token&lt;/code&gt; in your code.&lt;/p&gt;

&lt;p&gt;Be aware that this copy-and-pasting credentials does introduces a new risk in a multi-account scenario. Maybe you have different environments in different accounts. You have no way of seeing for which account you currently pasted the credentials. This means you best to copy-and-paste them with every script execution to prevent a mistake. Otherwise, you might accidentally run a script on the &lt;code&gt;production&lt;/code&gt; environment rendering it broken.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accidentally running destructive code
&lt;/h2&gt;

&lt;p&gt;Coincidentally Joris Conijn mentions this risks in his &lt;a href="https://xebia.com/blog/avoid-using-the-default-profile/"&gt;blog post&lt;/a&gt;. It is about accidentally running code that does something destructive without any warning prompt. It doesn't even have to be your own code, any downloaded code, for example in a package initialization, employing &lt;code&gt;boto3&lt;/code&gt; will just try and execute its payload. To mitigate this risk just don't use the &lt;code&gt;default&lt;/code&gt; profile, but set up the credentials in a named profile. That way any script not knowing about the profile name will not use it and it will try use the &lt;code&gt;default&lt;/code&gt; profile and render an error. (A really nasty script could parse the credentials file though and try to find a working one. The &lt;code&gt;boto3&lt;/code&gt; library will even help the malicious code by supplying &lt;code&gt;boto3.Session().available_profiles&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;It could also be your own code that is malicious. I once had a script that would remove the ingress rules of the default &lt;code&gt;SecurityGroup&lt;/code&gt; of every &lt;code&gt;Vpc&lt;/code&gt; in &lt;em&gt;all&lt;/em&gt; the accounts in my organization (this is a good practice). The code contains a condition to limit its destructive payload to only the &lt;code&gt;SecurityGroup&lt;/code&gt; that has &lt;code&gt;IsDefault=True&lt;/code&gt;. But one commenting &lt;code&gt;#&lt;/code&gt; before this &lt;code&gt;if&lt;/code&gt; would arm this code to become a threat of immense magnitude to the organization! (I would not even know where to begin recovering if this armed code would accidentally run...) &lt;/p&gt;

&lt;p&gt;This also shows the next risk over overly permissiveness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overly permissive sessions
&lt;/h2&gt;

&lt;p&gt;Most of the times the permissions you get from the profile or SSO are very generic or maybe even admin. Once you have a session using those credentials you basically script around with a fully loaded rifle to shoot in your own foot. I have found it to be a good practice to &lt;em&gt;scope down&lt;/em&gt; the permissions using a session.&lt;/p&gt;

&lt;p&gt;I usually have a function in my code for getting the boto3 session like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;sts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sts&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;account_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_caller_identity&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Account&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;creds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assume_role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;RoleArn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:iam::&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;account_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:role/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;role_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;RoleSessionName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my_session&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2012-10-17&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Statement&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Effect&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Allow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;organizations:DescribeOrganization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Resource&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;}]&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Credentials&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;aws_access_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AccessKeyId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;aws_secret_access_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SecretAccessKey&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;aws_session_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SessionToken&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only thing required is an IAM role that you can assume with enough permissions for your purpose.&lt;/p&gt;

&lt;p&gt;In larger scripts I often even pass in the policy I want for that section of the code. This way each section has its own &lt;em&gt;scoping down&lt;/em&gt; to further reduce the risks.&lt;/p&gt;

&lt;p&gt;When you start adding code to your script that uses other boto3 calls and you run the code you usually get an error elaborate error message mentioning the action and resource missing in the scoping down policy. You just evaluate them, add them to the policy and run the script again. This way you are constantly aware that you are doing things safely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Infrastructure as Code
&lt;/h2&gt;

&lt;p&gt;Infrastructure as Code (IaC) is the way to go when creating or changing things hosted in the cloud. There should be &lt;em&gt;source code&lt;/em&gt; in a repository indicating the &lt;em&gt;desired state&lt;/em&gt; of your infrastructure. Usually people consider CloudFormation a good format to express the desired state in. And ideally you would only want one format. But with CloudFormation you are limited to what it supports, unless you are willing to code or introduce some CustomResources into your infrastructure project. Coding CustomResources is not requires a fair amount of experience though. It might seem easy, but surfing around all the edge cases the CloudFormation process has is sometimes not so intuitive. And the CustomResources come with some responsibility of maintenance as well. The Lambda functions involved often sit around in your AWS account for years. The runtime it uses probably no longer supported when the time comes to destroy your resources. You might need to roll out updates to CustomResources and test wether they still cover your use-case.&lt;/p&gt;

&lt;p&gt;That why I often compromise the &lt;em&gt;one format&lt;/em&gt; rule. You can add a &lt;em&gt;yaml&lt;/em&gt; file to your repo containing some desired state and a &lt;em&gt;post deploy&lt;/em&gt; script in python that updates the target AWS account(s) to reflect the state from the yaml file. You set up this script to run in the same pipeline where your CloudFormation is deployed. This can be a lot simpler than juggling with custom resources and I think it is still called "Infrastructure as Code".&lt;/p&gt;

&lt;p&gt;Here are some examples where I chose to go for the &lt;em&gt;yaml-script&lt;/em&gt; approach (sometimes without yaml):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;delete all rules from default security groups in vpcs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;(re)setting IAM password policy&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;configuring the state of SecurityHub controls to ENABLED or DISABLED&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;updating confluence page with information about your infrastructure&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;account creation bootstrapping / destruction&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think as long as you put the actual state in separate files (like the yaml) you can still consider it to be Infrastructure as Code.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>boto3</category>
      <category>cloud</category>
      <category>python</category>
    </item>
    <item>
      <title>How to share your organizations ID</title>
      <dc:creator>Jacco Kulman</dc:creator>
      <pubDate>Sun, 12 Nov 2023 14:57:48 +0000</pubDate>
      <link>https://forem.com/aws-builders/how-to-share-your-organizations-id-4hl6</link>
      <guid>https://forem.com/aws-builders/how-to-share-your-organizations-id-4hl6</guid>
      <description>&lt;p&gt;As a cloud engineer you sometimes find yourself in a &lt;a href="https://en.wikipedia.org/wiki/Down_the_rabbit_hole"&gt;rabbit hole&lt;/a&gt;. Typically you fall from one problem into the next, and then the next and so on. Personally I quite enjoy the experience because you never know what you'll learn. The only real problem is that I sometimes forget why I got there in the first place. This is such an occasion.&lt;/p&gt;

&lt;p&gt;Here is my reconstruction of how I think I got here: I was trying to implement SSM parameter replication. At a certain point I needed to navigate the Organization tree. Then I found out that I have to create a resource policy on the Organization in the management account. And - if you want to do that using Infrastructure as Code - you can use the &lt;code&gt;AWS::Organizations::ResourcePolicy&lt;/code&gt; resource type for that. The AWS &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-organizations-resourcepolicy.html"&gt;documentation&lt;/a&gt; mentions the attributes of this resource includes its &lt;code&gt;arn&lt;/code&gt; and the &lt;code&gt;arn&lt;/code&gt; has the format &lt;code&gt;arn:aws:organizations::111111111111:resourcepolicy/o-exampleorgid/rp-examplepolicyid111&lt;/code&gt;. And because this includes the &lt;code&gt;o-exampleorgid&lt;/code&gt; which is a good candidate for such replication.&lt;/p&gt;

&lt;p&gt;Then I stumbled over a github &lt;a href="https://github.com/aws-cloudformation/cfn-language-discussion/issues/57"&gt;issue&lt;/a&gt; where a feature is discussed for CloudFormation to have an additional pseudo parameter for the Id of the Organization. I decided to dig a little further. Although rabbit holes can be quite deep your are never alone...&lt;/p&gt;

&lt;p&gt;In the github issue they are asking for an &lt;code&gt;{AWS::OrgId}&lt;/code&gt; pseudo parameter to be implemented. Having seen the &lt;code&gt;ResourcePolicy&lt;/code&gt;s attribute &lt;code&gt;Arn&lt;/code&gt; I thought I had a perfect solution for the stated problem so I decided to share that as a comment on the github issue. Here is my initial take at solving the problem:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(for readers only interested in the final solution and not so much for the narrative: skip this one and scroll down)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;OrganizationsResourcePolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Organizations::ResourcePolicy&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2012-10-17&lt;/span&gt;
          &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Sid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AllowReadonlyNavigateOrganization&lt;/span&gt;
              &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
              &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;AWS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
              &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;organizations:ListRoots&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;organizations:ListOrganizationalUnitsForParent&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;organizations:ListAccountsForParent&lt;/span&gt;
              &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
              &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;StringEquals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="s"&gt;aws:PrincipalOrgID": "${aws:PrincipalOrgID}"&lt;/span&gt;
  &lt;span class="na"&gt;OrganizationParameter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::SSM::Parameter&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/organizations/id&lt;/span&gt;
      &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Select&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Split&lt;/span&gt; 
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;OrganizationsResourcePolicy.Arn&lt;/span&gt;
      &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a nifty &lt;code&gt;Select&lt;/code&gt; on a &lt;code&gt;Split&lt;/code&gt; intrinsic I yank out the &lt;code&gt;Id&lt;/code&gt; of the organization and create an SSM parameter of it. If you bootstrap your management account with this you'll have this ssm parameter available for use as parameter default or dynamic parameter in CloudFormation. But... sadly... only in the same region and only in this account. But maybe my upcoming &lt;em&gt;SSM parameter replication&lt;/em&gt; feature will save the day! Well... maybe there is an easier solution.&lt;/p&gt;

&lt;p&gt;While reading the documentation I saw that &lt;code&gt;AWS::Organizations::Organization&lt;/code&gt; is a better candidate to use when solving this because apart from a direct &lt;code&gt;Id&lt;/code&gt; attribute it also has the &lt;code&gt;RootId&lt;/code&gt; attribute which can be very useful. But this resource cannot be simply used when you already have an Organization in your account. (See how we fell into the next problem here?) To solve this you have to &lt;code&gt;import&lt;/code&gt; the Organization resource. To do that you need to create a template where it is the only resource, add a &lt;code&gt;DeletionPolicy&lt;/code&gt; and use the import feature.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Organization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;DeletionPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Retain&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Organizations::Organization&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;FeatureSet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ALL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have done the initial import you can add more resources and &lt;code&gt;update&lt;/code&gt; the stack. In this stack you can do all you want with all the attributes the Organization has: &lt;code&gt;Arn,  Id, ManagementAccountArn, ManagementAccountEmail, ManagementAccountId and RootId&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So the goal is to let other accounts/regions know about the &lt;code&gt;Id&lt;/code&gt;. Going through the list of CloudFormation resources I also encountered &lt;code&gt;StackSet&lt;/code&gt; and though that might be a good candidate to do the distributing. After fiddling around a bit I came up with this:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(For the skipping reader you can't use this template directly. You'll need to start with importing the template right above this one)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Regions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CommaDelimitedList&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1,eu-central-1,eu-west-1,eu-west-2,eu-west-3&lt;/span&gt;
&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Organization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;DeletionPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Retain&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Organizations::Organization&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;FeatureSet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ALL&lt;/span&gt;
  &lt;span class="na"&gt;ParameterStackSet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::CloudFormation::StackSet&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;StackSetName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OrganizationParameterStack&lt;/span&gt;
      &lt;span class="na"&gt;PermissionModel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SERVICE_MANAGED&lt;/span&gt;
      &lt;span class="na"&gt;AutoDeployment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;True&lt;/span&gt;
        &lt;span class="na"&gt;RetainStacksOnAccountRemoval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;True&lt;/span&gt;
      &lt;span class="na"&gt;ManagedExecution&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Active&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;StackInstancesGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;DeploymentTargets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;OrganizationalUnitIds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;Organization.RootId&lt;/span&gt;
          &lt;span class="na"&gt;Regions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;Regions&lt;/span&gt;
      &lt;span class="na"&gt;TemplateBody&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"AWSTemplateFormatVersion": "2010-09-09",&lt;/span&gt;
            &lt;span class="s"&gt;"Resources": {&lt;/span&gt;
              &lt;span class="s"&gt;"AtLeastOneResource": {&lt;/span&gt;
                &lt;span class="s"&gt;"Type": "AWS::CloudFormation::WaitConditionHandle"&lt;/span&gt;
              &lt;span class="s"&gt;}&lt;/span&gt;
            &lt;span class="s"&gt;},&lt;/span&gt;
            &lt;span class="s"&gt;"Outputs": {&lt;/span&gt;
              &lt;span class="s"&gt;"OrganizationId": {&lt;/span&gt;
                &lt;span class="s"&gt;"Description": "Id of the Organization",&lt;/span&gt;
                &lt;span class="s"&gt;"Value": "${OrganizationId}",&lt;/span&gt;
                &lt;span class="s"&gt;"Export": {&lt;/span&gt;
                  &lt;span class="s"&gt;"Name": "organizationId"&lt;/span&gt;
                &lt;span class="s"&gt;}&lt;/span&gt;
              &lt;span class="s"&gt;}&lt;/span&gt;
            &lt;span class="s"&gt;}&lt;/span&gt;
          &lt;span class="s"&gt;}&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;OrganizationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;Organization.Id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works!!&lt;/p&gt;

&lt;p&gt;I decided to go for an &lt;code&gt;output&lt;/code&gt; instead of a ssm parameter. Because outputs are a little stricter (better?) You can't change or remove the while they are imported. (This is a feature I am also looking for my &lt;em&gt;ssm parameter replication&lt;/em&gt; feature.) Internally CloudFormation does reference counting on them.&lt;/p&gt;

&lt;p&gt;A problem with this solution is that the StackSet will not be distributed to the management account itself. Maybe you can get it to do that if you are not using &lt;code&gt;SERVICE_MANAGED&lt;/code&gt; but I refused to fall further down ;-)&lt;/p&gt;

&lt;p&gt;Note: I did experimented &lt;code&gt;Fn::ToJsonString&lt;/code&gt; and have it all in &lt;code&gt;yaml&lt;/code&gt; which works but you also need the extra &lt;code&gt;Transform: 'AWS::LanguageExtensions'&lt;/code&gt;. Which somehow needs all the IAM warning checkboxes with &lt;em&gt;every&lt;/em&gt; deploy. So I decided not to use that. No more time for additional holes to fall into because it is almost time to cook dinner.&lt;/p&gt;

&lt;p&gt;You could add many parameters here and they would be shared with the whole organization across regions. Is my &lt;code&gt;ssm parameter replicator&lt;/code&gt; feature obsolete now? I think not because this solution still cannot handle my use case: create resource in one account, share an attribute of that resource across (part) of the organization, have the use of it reference counted, handle race conditions correctly. So I guess I will still be stuck implementing it if I can keep out of rabbit holes long enough.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudformation</category>
      <category>organizations</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Using undocumented AWS APIs</title>
      <dc:creator>Jacco Kulman</dc:creator>
      <pubDate>Sun, 22 Oct 2023 13:04:13 +0000</pubDate>
      <link>https://forem.com/aws-builders/using-undocumented-aws-apis-12c6</link>
      <guid>https://forem.com/aws-builders/using-undocumented-aws-apis-12c6</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/binxio/aws-iamv2"&gt;TL;DR just give me the code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While evaluating some existing IAM policies in a codebase, I found myself repeating the same steps over and over again: navigate Google and search &lt;code&gt;iam actions servicename&lt;/code&gt; and look up information about the actions used.&lt;/p&gt;

&lt;p&gt;Pro tip: it is much easier to just bookmark this one: &lt;a href="https://docs.aws.amazon.com/service-authorization/latest/reference/reference_policies_actions-resources-contextkeys.html"&gt;https://docs.aws.amazon.com/service-authorization/latest/reference/reference_policies_actions-resources-contextkeys.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The information I needed for my policy validation work is quite simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;what are all the services?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;what are all the actions a service has?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;what actions match a pattern like "Desc*" for a service?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;what are mandatory and optional resources for each action?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;what condition keys can be used?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;is the action readonly, readwrite or something else?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first place to go to find out if this information is somehow exposed would be the AWS SDKs. I looked through the boto3 documentation on the &lt;code&gt;iam&lt;/code&gt; service and came up empty.&lt;/p&gt;

&lt;p&gt;After doing a lot of policies using the manual process, I remembered that AWS has a policy-editing tool in the console that seems to be using the information I was looking up manually. So I started my adventure by trying to automate my struggles.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--U1V0rSNS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vfweb3j2tbo9ttn3ewrv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U1V0rSNS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vfweb3j2tbo9ttn3ewrv.png" alt="Image description" width="800" height="716"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I decided to invest some time in the policy editor, which was using some kind of API I could use to automate some things. So with the Chrome Inspect pane using the network tab, I saw a lot of http requests to:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://us-east-1.console.aws.amazon.com/iamv2/api/iamv2"&gt;https://us-east-1.console.aws.amazon.com/iamv2/api/iamv2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After some experimentation and fiddling with cookies and CSRF tokens, I found out how this undocumented API worked. So I cooked up a little Python to automate that. Since it is only 80 lines of code, I'll share it here. I will probably make an installable Python package of it soon. The repository with the code and some examples is &lt;a href="https://github.com/binxio/aws-iamv2"&gt;https://github.com/binxio/aws-iamv2&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;bs4&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;

&lt;span class="c1"&gt;# composePolicy, decomposePolicy, checkMultiMFAStatus, createX509, cuid, generateKeyPairs
&lt;/span&gt;&lt;span class="n"&gt;methods&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"services"&lt;/span&gt;                    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"actions"&lt;/span&gt;                     &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"serviceName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"RegionName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"eu-central-1"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="s"&gt;"resources"&lt;/span&gt;                   &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"contextKeys"&lt;/span&gt;                 &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"globalConditionKeys"&lt;/span&gt;         &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"getServiceLinkedRoleTemplate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"serviceName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="s"&gt;"policySummary"&lt;/span&gt;               &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"policyDocument"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="s"&gt;"validate"&lt;/span&gt;                    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"policy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConsoleSession&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;boto3_session&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_credentials&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_credentials&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_signed_in&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_csrf_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_rsession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__getattribute__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;make_lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_api_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;make_lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__getattribute__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;signin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_rsession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"https://signin.aws.amazon.com/federation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="s"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"getSigninToken"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Session"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                    &lt;span class="s"&gt;"sessionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;access_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s"&gt;"sessionKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;secret_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s"&gt;"sessionToken"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s"&gt;"SigninToken"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_rsession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"https://signin.aws.amazon.com/federation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="s"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"login"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Issuer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"https://console.aws.amazon.com/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"SigninToken"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_rsession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"https://us-east-1.console.aws.amazon.com/iamv2/home#"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"eu-central-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"hashArgs"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"html.parser"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;find_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"meta"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"awsc-csrf-token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_csrf_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_signed_in&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_api_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_signed_in&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_rsession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"https://us-east-1.console.aws.amazon.com/iamv2/api/iamv2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"X-CSRF-Token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_csrf_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="s"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="s"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"/prod/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"us-east-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"params"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
                &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="s"&gt;"contentString"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things about this code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;ConsoleSession&lt;/code&gt; class takes a &lt;code&gt;boto3.Session&lt;/code&gt; as input. This session needs no actual rights to AWS.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The code might look a bit strange because I wanted to make it as a &lt;em&gt;dynamic class&lt;/em&gt; so I only had to add one line to implement an extra API endpoint. This uses the &lt;code&gt;__getattribute__&lt;/code&gt; override and the &lt;code&gt;methods&lt;/code&gt; object.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I use the &lt;code&gt;requests&lt;/code&gt; module and start a &lt;code&gt;requests.Session()&lt;/code&gt; that does much of the heavy lifting handling cookies needed for the http requests to succeed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To fetch the &lt;code&gt;awsc-csrf-token&lt;/code&gt; from the page I use &lt;code&gt;BeautifulSoup&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The method in &lt;code&gt;methods&lt;/code&gt; were not all discover by me, I did a search on &lt;code&gt;iamv2&lt;/code&gt; on github and found some json files that were already detailing this API.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Since the information retrieved for the API calls I needed does not change per request I implemented a simple caching feature. For some methods like &lt;code&gt;policySummary&lt;/code&gt; and &lt;code&gt;validate&lt;/code&gt; this might not be optimal.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The actual signing in is done using three HTTP requests:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Getting a &lt;code&gt;SigninToken&lt;/code&gt; from &lt;code&gt;signin.aws.amazon.com/federation&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use the token to &lt;code&gt;login&lt;/code&gt; to &lt;code&gt;https://console.aws.amazon.com/&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Retrieve &lt;code&gt;https://us-east-1.console.aws.amazon.com/iamv2/home#&lt;/code&gt; to get the &lt;code&gt;awsc-csrf-token&lt;/code&gt; from the page.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Example usage
&lt;/h2&gt;

&lt;p&gt;The code below demonstrates example usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;iamv2&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ConsoleSession&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;re&lt;/span&gt;

&lt;span class="n"&gt;awssvcs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;console_session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_iam_info&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;console_session&lt;/span&gt;
    &lt;span class="n"&gt;boto_session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'us-east-1'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;console_session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConsoleSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;boto_session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;services&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;console_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"serviceName"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;awssvcs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;awssvcs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"parts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;awssvcs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;"parts"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_statement_actions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NotAction"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;reverse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"NotAction"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;statement&lt;/span&gt;
    &lt;span class="n"&gt;reverse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"Deny"&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;
    &lt;span class="n"&gt;actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;actions&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;act&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;':'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s"&gt;"Actions"&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;awssvcs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;awssvcs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;"Actions"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;console_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;awssvcs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;"parts"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;"serviceKeyName"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;actrgx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;act&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'[A-Za-z]+'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;svc_action&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;awssvcs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;"Actions"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actrgx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;svc_action&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"actionName"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IGNORECASE&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;svc_action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_policy_actions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;statement&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;get_statement_actions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;get_policy_actions&lt;/code&gt; will simply list all the actions allowed by the statements in the policy. Here it is in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="s"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
            &lt;span class="s"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"ReadOnlyCloudTrail"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="s"&gt;"NotAction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"cloudtrail:De*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="s"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"*"&lt;/span&gt;
        &lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;get_iam_info&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;statement_actions&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;get_policy_actions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;statement_actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statement_actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"actionName"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;statement_actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"actionName"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s"&gt;", "&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"actionGroups"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will give the following list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DeleteChannel                            ReadWrite
DeleteEventDataStore                     ReadWrite
DeleteResourcePolicy                     ReadWrite
DeleteServiceLinkedChannel               ReadWrite
DeleteTrail                              ReadWrite
DeregisterOrganizationDelegatedAdmin     ReadWrite
DescribeQuery                            ReadOnly, ReadWrite
DescribeTrails                           ReadOnly, ReadWrite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that this policy allows some actions that are not read-only. You could use this code in tests being evaluated in your pipeline to make sure you never accidentally allow non-readonly actions in this policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future
&lt;/h2&gt;

&lt;p&gt;I will make an installable Python package from the API part. Also, I have some ideas for some neat policy tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;check for common mistakes in policies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;generate readonly service statements for in a permissions boundary&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>python</category>
      <category>iam</category>
    </item>
    <item>
      <title>Regain control over orphaned resources with CDK</title>
      <dc:creator>Jacco Kulman</dc:creator>
      <pubDate>Wed, 08 Mar 2023 22:23:37 +0000</pubDate>
      <link>https://forem.com/aws-builders/regain-control-over-orphaned-resources-with-cdk-gm6</link>
      <guid>https://forem.com/aws-builders/regain-control-over-orphaned-resources-with-cdk-gm6</guid>
      <description>&lt;p&gt;Sometimes AWS resources are orphaned. This means the CloudFormation stack is destroyed but the resource is retained. This can happen either because the CloudFormation template specified this wish in the &lt;code&gt;DeletionPolicy&lt;/code&gt; or after receiving an error from CloudFormation during the destruction and you decided to delete the stack anyway awknowledging that the resource in question will be retained.&lt;/p&gt;

&lt;p&gt;It is entirely possible to work with the orphaned resource because you can always refer to it using its &lt;code&gt;Arn&lt;/code&gt;. In CDK you use the &lt;code&gt;from...&lt;/code&gt; static methods for doing that. But this will not give you back the control over the properties of the orphaned resource.&lt;/p&gt;

&lt;p&gt;Orphaned resources become troublesome to deal with in practice though. One example is that, when the maturity level of you organisation rises, you get confronted with a new rule saying that all your S3 buckets should have certain security measures in place like enforcing SSL access or KMS encryption. Now if also click-ops (managing resources using the AWS console) is banished by your Security Officer, you need a way to regain control over the orphaned buckets via code.&lt;/p&gt;

&lt;p&gt;CloudFormation has this feature for a while already, but for CDK project you have to use an experimental feature. Here are the steps you have to take to adopt an ophaned resource:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Decide which application / stack should become the new parent for your resource&lt;/li&gt;
&lt;li&gt;Create the resource in that stack using CDK code (the exact properties do not matter for now)&lt;/li&gt;
&lt;li&gt;Synth your project and lookup the logical id of the resource in the generated stack in &lt;code&gt;CDK.OUT&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;mapping.json&lt;/code&gt; in which you link the logical id to the physical id of the existing resource&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;cdk import -m mapping.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Lookup the stack in the console and perform a drift detection&lt;/li&gt;
&lt;li&gt;Now adjust the code in you CDK project so the resource will match the drift&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;cdk deploy&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It is very important to understand that when running step 5 nothing other than the resource addition may have changed in your stack! If more things changed you will get an error message.&lt;/p&gt;

&lt;p&gt;You can also work without the &lt;code&gt;mapping.json&lt;/code&gt; file, but the &lt;code&gt;cdk import&lt;/code&gt; process becomes interactive then, making it unsuitable for in a CI/CD pipeline. The json file should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"LogIdOfTheResourceYouLookedUp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"BucketName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-existing-bucket-name"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I only tested this with a bucket, the actual link to the physical id property might differ per resource type.&lt;/p&gt;

&lt;p&gt;In a CI/CD pipeline automation you might choose do an &lt;code&gt;cdk import&lt;/code&gt; step in stead of a &lt;code&gt;cdk deploy&lt;/code&gt; whenever the &lt;code&gt;mapping.json&lt;/code&gt; is around. Another thing to note is that this currently only works if you either have a project with one stack or you specify that stack you want to perform the &lt;code&gt;cdk import&lt;/code&gt; with. I imagine you could create a &lt;code&gt;stackname-mapping.json&lt;/code&gt; and have your pipeline perform the stack selecting.&lt;/p&gt;

&lt;p&gt;Joris Conijn already wrote a nice article about migrating resources between CDK stacks without using the experimental feature &lt;a href="https://xebia.com/blog/migrate-resources-across-cdk-stacks/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The experimental cdk feature is described &lt;a href="https://github.com/aws/aws-cdk-rfcs/issues/52"&gt;here&lt;/a&gt;. The rfc is still open so you might be able to track or influence how it will stabilize.&lt;/p&gt;

</description>
      <category>cdk</category>
      <category>aws</category>
      <category>cloudformation</category>
    </item>
    <item>
      <title>re:Invent Queueing Guide</title>
      <dc:creator>Jacco Kulman</dc:creator>
      <pubDate>Thu, 08 Dec 2022 18:56:10 +0000</pubDate>
      <link>https://forem.com/aws-builders/reinvent-queueing-guide-47fp</link>
      <guid>https://forem.com/aws-builders/reinvent-queueing-guide-47fp</guid>
      <description>&lt;p&gt;Having a great live re:Invent has a lot to do with your success rate at getting to see the sessions you want.&lt;/p&gt;

&lt;p&gt;I was quite disappointed when I discovered that all the sessions I wanted to see were already fully booked 6 hours after reserving seats was possible for re:Invent 2022. I felt "lucky" that I could get on the "waiting-list" for 3 sessions...&lt;/p&gt;

&lt;p&gt;But on Monday morning freshly arrived in Las Vegas I decided to use my "Lowlands-strategy". Lowlands is a popular music festival in the Netherlands with around 60.000 visitors and 5 big stages and a few smaller ones. If you want to see a good band play there, you have to go at least one hour before the starting time to the venue and camp there with the groupies until the show starts.&lt;/p&gt;

&lt;p&gt;On re:Invent this strategy worked out perfectly for me. I never had a full room, because I was always near the front of the walk-up queue. Being one hour in the line did not feel like a huge sacrifice either. I often had my laptop with me and otherwise there was always someone to have a nice "preview"-chat with about the session we were waiting for. &lt;/p&gt;

&lt;p&gt;The "downside" of the strategy is that the achieved number sessions per day could be a bit lower than you anticipated. I achieved 3-4 sessions per day this way. But hey I never expected to see any of them :-)&lt;/p&gt;

&lt;p&gt;Details of the queue: &lt;/p&gt;

&lt;p&gt;60 min before session - start forming 2 queues "walk-up" and "reserved". Try find a spot near a wall so you can give your back a rest.&lt;/p&gt;

&lt;p&gt;30 min before session - start of allowing reserved seats&lt;/p&gt;

&lt;p&gt;10 min before session - start of allowing in walk-ups&lt;/p&gt;

&lt;p&gt;Note: After being let in, you can leave for a toiletbreak and come back in :-) Before being let in you can of course agree with your neighbours that you can have your spot back.&lt;/p&gt;

&lt;p&gt;Note: Being on the "waiting-list" for a session does &lt;em&gt;not&lt;/em&gt; mean you will have any priority when doors to the room are opened (30 minutes before the start of the session). &lt;/p&gt;

&lt;p&gt;Note: If you have a reserved seat and come after 10 minutes before the start of the session: you will be sent to the end of the "walk-up" queue and probably not make it in.&lt;/p&gt;

&lt;p&gt;Note: I hope this blog does not inspire everyone to adopt this strategy for next year's re:Invent, because I guess it doesn't work then....&lt;/p&gt;

&lt;p&gt;Anyway hope to see you next year on re:Invent ;-)&lt;/p&gt;

</description>
      <category>queueing</category>
      <category>reinvent</category>
      <category>aws</category>
    </item>
    <item>
      <title>AWS StepFunction Distributed Map</title>
      <dc:creator>Jacco Kulman</dc:creator>
      <pubDate>Thu, 08 Dec 2022 15:57:12 +0000</pubDate>
      <link>https://forem.com/aws-builders/aws-stepfunction-distributed-map-3m4f</link>
      <guid>https://forem.com/aws-builders/aws-stepfunction-distributed-map-3m4f</guid>
      <description>&lt;p&gt;The last day of re:Invent 2022 still had some nice talks about the newly released features. This one is an extension of the existing Map step that StepFunctions already has.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why
&lt;/h3&gt;

&lt;p&gt;The existing Map step has some limitations regarding input/output size (256KB), logging the execution history (25000), parallelism (max 40). It is good enough to execute smaller tasks in parallel. If you want larger tasks you can of coarse use S3 as a storage and implement the loading and saving of data yourself in the Lambda function(s) but since this is a quit common pattern AWS decided the extend the functionality of the Map step.&lt;/p&gt;

&lt;h3&gt;
  
  
  What
&lt;/h3&gt;

&lt;p&gt;When using the new distributed processing mode you can specify a S3 source (either on file, objects with a prefix or the whole bucket). The internals of the Map step will then be treated a a separate workflow.&lt;/p&gt;

&lt;p&gt;At the start of the execution of the parent StepFunction the information from S3 is gathered using ListObjectsV2 and then, using the parallellism and batching you specify, the child StepFunction is called. All attributes from ListObjectsV2 will be passed into the child StepFunction.&lt;/p&gt;

&lt;p&gt;The maximum parallelism is increased up to 10000 parallel executions of the child StepFunction. The input and output limitations are no longer there. And because the inner StepFunction has its own execution history also this limitation is mitigated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other sources than S3
&lt;/h3&gt;

&lt;p&gt;Other sources are currently not supported. In order to use other sources you have to set up some steps in the parent StepFunction to prepare some S3 objects to use as input.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;p&gt;Because the amount of executions can be very large you can specify an error threshold. If the number of errors is lower then the threshold the Map step will still succeed.&lt;/p&gt;

&lt;p&gt;In the Map step you can also specify an export location in S3. The results of running the Map step will be reported there (manifest.json, Succeeded.json, Failed.json, Pending.json). You can use a step after the Map step in the parent StepFunction to process these output files to the result you require.&lt;/p&gt;

&lt;p&gt;In the console you can follow the progress of your distributed map step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use cases
&lt;/h3&gt;

&lt;p&gt;When doing map-reduce like operations on large sets of data this can be a very nice architecture. No need for spark or haddoop instances but a completely serverless solution.&lt;/p&gt;

&lt;p&gt;To get a grasp of the size oof the workload you can process using this functionality. In the demo AWS presented a case where over 500000 S3 objects were processed in batches of 250 objects with a paralellism of 3000 parallel lambda executions, each lambda would take 16 seconds to process. The total execution time was under 2:30 minutes.&lt;/p&gt;

&lt;p&gt;Kinesis streamed to S3 has a directory structure very suitable to process in this way. If the S3 objects are too large to handle in the lambda function you can use for example the AWS Lambda Powertools to have the lambda process consequtive parts of the objects.&lt;/p&gt;

&lt;p&gt;Picture is from a hike we took on the day of arrival in Las Vegas. The location is called Valley of Fire.&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>AWS Application Composer</title>
      <dc:creator>Jacco Kulman</dc:creator>
      <pubDate>Wed, 07 Dec 2022 05:48:38 +0000</pubDate>
      <link>https://forem.com/aws-builders/aws-application-composer-19c1</link>
      <guid>https://forem.com/aws-builders/aws-application-composer-19c1</guid>
      <description>&lt;p&gt;On re:Invent 2022 Werner Vogels announced the availability of the preview of AWS Application Composer. I am a big fan of &lt;a href="https://scratch.mit.edu//"&gt;Scratch&lt;/a&gt;, a visual programming language to teach young children how to program, so I was very interested to see what this new feature looked like.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why
&lt;/h3&gt;

&lt;p&gt;According to the presenters a lot of people struggle learning CloudFormation and Infrastructure as Code frameworks in general. This new tool should make it easier for beginning cloud engineers to get started.&lt;/p&gt;

&lt;h3&gt;
  
  
  What
&lt;/h3&gt;

&lt;p&gt;AWS Application Composer is basically a smart designer canvas on which resources can be dropped and connected. While designing Composer keeps a CloudFormation template in sync on you local development setup using your browsers capability to manage files.&lt;/p&gt;

&lt;p&gt;After dropping a resource on the canvas it will automatically be configure with "sane" settings. Also in some cases more than one resource will be created under the hood. The UI hides some more complicated configuration options to not complicate things too much. You can however also go into the template using your favorite code editor and add or change more complicated stuff. Composer will notice changes and update the template accordingly.&lt;/p&gt;

&lt;p&gt;Connections between resources take care of several things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;set default IAM policies&lt;/li&gt;
&lt;li&gt;set environment variables (for Lambna) &lt;/li&gt;
&lt;li&gt;manage event subscriptions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The CloudFormation generated uses the SAM format (Serverless Application Model). Ryan Coleman, who also is the Product Manager for SAM. SAM's smart parameterizable policies are used to keep also the generated CloudFormation readable.&lt;/p&gt;

&lt;p&gt;The philosophy seems to be to keep the CloudFormation deployable while designing. That is also why lambda's will be created with some template code already. SAM will take care of packaging and deploying the code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Demo awesomeness
&lt;/h3&gt;

&lt;p&gt;During the demo at re:Invent Ryan had setup SAM to "watch" the template he was working on. So while dragging and dropping en designing SAM would behind the scene already validate and deploy the design using the generated CloudFormation. This is really great for these kind of demo's if you quickly want to show how things work without going to all the consoles needed to configure the necessary resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Current features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;import existing CloudFormation templates&lt;/li&gt;
&lt;li&gt;grouping resources into named groups (stored in metadata)&lt;/li&gt;
&lt;li&gt;lambda scaffolding&lt;/li&gt;
&lt;li&gt;managing event subscriptions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Future
&lt;/h3&gt;

&lt;p&gt;The following features are not yet in the product but when asked Ryan said they could be prioritized based on customer feedback:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;support for other languages than CloudFormation (CDK, terraform)&lt;/li&gt;
&lt;li&gt;working with multiple stacks and the interdependencies&lt;/li&gt;
&lt;li&gt;importing SSM parameters and other "lookup"&lt;/li&gt;
&lt;li&gt;availability as a plugin for VSCode (and others)&lt;/li&gt;
&lt;li&gt;copy/paste on the designer&lt;/li&gt;
&lt;li&gt;support for more resources&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;This tool in its current state is a great way to get started with learning CloudFormation. For quickly demonstrating or prototyping of a serverless architecture the tool is really great.&lt;/p&gt;

&lt;p&gt;Probably for maintaining production CloudFormation stacks the product is currently too limited.&lt;/p&gt;

&lt;p&gt;I was very impressed though with how carefully this product has been set up. Handling any template including ones with yet unsupported resource types (those will show as read-only resources) and keeping all manual CloudFormation edits in the design seems to be a promise that some day this might become the mature enough to manage all your CloudFormation stacks with.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>news</category>
      <category>prototype</category>
    </item>
    <item>
      <title>AWS Inspector for AWS Lambda</title>
      <dc:creator>Jacco Kulman</dc:creator>
      <pubDate>Tue, 06 Dec 2022 12:26:35 +0000</pubDate>
      <link>https://forem.com/aws-builders/aws-inspector-for-aws-lambda-3cl7</link>
      <guid>https://forem.com/aws-builders/aws-inspector-for-aws-lambda-3cl7</guid>
      <description>&lt;p&gt;Fresh from re:Invent 2022. Just left the pop-up session organized just after the announcement by the AWS Inspector team Rick Anthony and Kashish Wadhwa. After the session was able to ask Rick Anthony a few questions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is this important
&lt;/h3&gt;

&lt;p&gt;More and more customers are moving to serverless workloads on AWS, moving away from EC2 instances and containers. Lambda usage is increasing every year and there are hardly any native security features for this service. This is why it becomes critical to address these vulnerabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does inspector do
&lt;/h3&gt;

&lt;p&gt;Inspector can already scan EC2 instances and ECR images for vulnerabilities. Now scanning Lambdas is also supported. Security Hub can be configured to accept the findings from Inspector so you can have one place where all security findings will be aggregated.&lt;/p&gt;

&lt;p&gt;Inspector decides what and when to scan instead of you scheduling. Also brand new vulnerabilities can be reason for AWS Inspector to decide to rescan. In the case of Lambda this means whenever the function is created or updated a scan will be performed. When a lambda is not called the last 90 days it will no longer be scanned (mothly cost will then also no longer apply). Currently scanning for the following run-times is supported: Python, nodejs and Java. Also scanning layers is supported, findings from layers will be reported for all functions where the layer is used and the finding will indicate that the cause of the vulnerability is from a layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vulnerabilities
&lt;/h3&gt;

&lt;p&gt;Vulnerabilities in Lambda are not related to the operating system (unless you are using custom run-times, which are not supported by the new feature) but come from the packages used for the programming language chosen. If, for example, you are using Java run-time and are using the log4j package you could have a vulnerability if you are using the unpatched version of the library. Inspector scans the artifacts the package tooling leaves behind (package.json for nodejs projects for example).&lt;/p&gt;

&lt;h3&gt;
  
  
  Exploitability
&lt;/h3&gt;

&lt;p&gt;Not all vulnerabilities in Lambda can be exploited easily because the OS firecracker is well hardened. Also the attack plane is limited because calling a lambda requires IAM authorization when calling it the regular way. If you are using the new function URL functionality though without authentication the attack plane is a little larger. Also if called through API Gateway and you are not using the request validation you could be vulnerable to an attack. Inspector takes into account the network reachability of the lambda in the score of the finding. The resulting score gives an indication of the priority with which this finding should be treated. Currently the AssumeRole policy of the Lambda execution role is not taken into account for the scoring and also the fact that FunctionUrls are used or not. AWS monitors 50 sources that publish vulnerabilities and they add some intelligence to it. The score which also takes into account the exploitabilty. A network scan is also done where the VPC edges are inspected for network reachability. It is correlated with CVE data. If the CVE is only exploitable remotely but the resource is local, the score is lowered.&lt;/p&gt;

&lt;h3&gt;
  
  
  Live runtimes
&lt;/h3&gt;

&lt;p&gt;Lambda containers are recycle after a period of time so when successfully attacked the attack needs to be re-executed after a recycle. But if the vulnerability involves code injection an attacker can basically infect each newly spun up lambda execution environment. The running lambda environments themselves are not scanned. Like with ECR this is an agentless solution. This also means that if you install extra python packages using a sub-command in python these will not be detected. (You shouldn't do that).&lt;/p&gt;

&lt;h3&gt;
  
  
  What should you do if there are findings
&lt;/h3&gt;

&lt;p&gt;If you have a vulnerability in a Lambda the following steps should be taken:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;inspect the Lambda code if the vulnerable part of the library is actually used. If not you could suppress the finding.&lt;/li&gt;
&lt;li&gt;If you find that the vulnerability can be exploited. The lambda needs to be redeployed using patched libraries and packages.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Enabling for the Organization
&lt;/h3&gt;

&lt;p&gt;Enabling Lambda scanning by Inspector for your whole organization is just a few clicks: In the delegated admin account check the checkbox to include lambda scanning (also check the box for auto-enabling for new accounts). Be aware though that the pricing if $0.30 per function per month so you might want to exclude some functions before enabling this for all your accounts. &lt;/p&gt;

&lt;h3&gt;
  
  
  Current features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;nodejs, python and java runtimes&lt;/li&gt;
&lt;li&gt;lastest version scanning on update or create&lt;/li&gt;
&lt;li&gt;rescanning when there are new threats&lt;/li&gt;
&lt;li&gt;AWS Organizations support&lt;/li&gt;
&lt;li&gt;network reachability included in score&lt;/li&gt;
&lt;li&gt;scoring the vulnerabilities (priority)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Future
&lt;/h3&gt;

&lt;p&gt;For the future the Rick from the Inspector team said they are looking into supporting the following features in the future:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add an option to scan all versions of the Lambda (currently only latest is scanned)&lt;/li&gt;
&lt;li&gt;adding lambda accessability (AssumeRolePolicy of the execution role or FunctionUrls) might be included in the scoring calculation&lt;/li&gt;
&lt;li&gt;making the stale function scanning period adjustable&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
