<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Mihirsaurkar on Medium]]></title>
        <description><![CDATA[Stories by Mihirsaurkar on Medium]]></description>
        <link>https://medium.com/@mihirsaurkar?source=rss-2c7b00e38dbd------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*kNnenDjr44b5HkK1LfSXvg.jpeg</url>
            <title>Stories by Mihirsaurkar on Medium</title>
            <link>https://medium.com/@mihirsaurkar?source=rss-2c7b00e38dbd------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 21:55:29 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@mihirsaurkar/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Taking Glue To Next Level: Cross-Account, Cross-Region Data Pipelines with dbt-Glue and Apache…]]></title>
            <link>https://medium.com/@mihirsaurkar/taking-glue-to-next-level-cross-account-cross-region-data-pipelines-with-dbt-glue-and-apache-2d430595b523?source=rss-2c7b00e38dbd------2</link>
            <guid isPermaLink="false">https://medium.com/p/2d430595b523</guid>
            <category><![CDATA[dbt]]></category>
            <category><![CDATA[data-engineering]]></category>
            <category><![CDATA[data-lake]]></category>
            <category><![CDATA[etl]]></category>
            <category><![CDATA[aws-glue]]></category>
            <dc:creator><![CDATA[Mihirsaurkar]]></dc:creator>
            <pubDate>Fri, 15 Nov 2024 16:32:35 GMT</pubDate>
            <atom:updated>2024-11-15T19:57:43.316Z</atom:updated>
            <content:encoded><![CDATA[<h3>Taking Glue To Next Level: Cross-Account, Cross-Region Data Pipelines with dbt-Glue and Apache Iceberg</h3><figure><img alt="AI-generated: Oranges and Peaches sharing data" src="https://cdn-images-1.medium.com/max/1024/1*q9jfVdCGVSTdoWRh50nuyQ.png" /></figure><p>In real-world scenarios, data pipelines seldom operate within the confines of a single AWS account or region. Modern data lakes scale and often transcend geographical and service boundaries. With dbt-glue, we&#39;ve already demonstrated how to build a data pipeline that is database engine agnostic. Using dbt, you can easily configure projects to interact with diverse data warehouses and databases, creating a unified, streamlined data transformation process while maintaining data lineage.</p><p>As organizations grow, new opportunities and partnerships naturally come up. This often involves sharing data assets with new partners or across different departments. If you’re lucky, your partner organization might already be using the same cloud provider. However, assuming that all data and services will reside in the same region is, more often than not, unrealistic.</p><p>With that back-drop let&#39;s discuss strategy to extend our existing AWS Glue data pipeline using dbt and Airflow.</p><h3>The Challenge</h3><p>Let’s consider the following scenario, Imagine your organization, <em>Oranges</em>, has recently signed a memorandum of understanding (MOU) with another organization, <em>Pears</em>, to share specific data assets. <em>Oranges</em> already has a well-established data lake set up in a AWS region. The agreement specifies that certain datasets from <em>Oranges </em>will be shared with <em>Pears</em>, who will store and process this data in their own AWS account, located in a different region.</p><p>This might sound straightforward, but there’s a catch: <em>Oranges</em> has invested heavily in their current data lake infrastructure, and a lift-and-shift to another region or a new tool isn’t feasible. Simply copying files between accounts isn’t a viable solution either; <em>Oranges</em> wants to maintain incremental data processing, apply safeguards, and control what data is handed over. This means we need a solution that is far more sophisticated than a basic file transfer.</p><p>With that Oranges and Pears agreed on setting up a dbt-glue job that would select data from certain tables in Oranges AWS account region us-west-1 and write the data into Pears AWS account in region us-east-1</p><h3>The Solution</h3><p>After careful consideration, <em>Oranges</em> and <em>Pears</em> agreed on a strategy to set up a dbt-glue job. This job would read data from specific tables in <em>Oranges</em>’ AWS account ( assume region us-west-1) and write the output directly to <em>Pears</em>’ AWS account in a different region (assume us-east-1). Here’s how they tackled it:</p><ol><li><strong>Glue Catalog Setup:</strong> <em>Oranges</em> configured an AWS Glue Data Catalog in the us-east-1 region to manage metadata for the new pipeline.</li><li><strong>Existing ELT Pipelines:</strong> The current ELT pipelines run daily, writing Iceberg tables to S3 buckets in us-west-1. These tables are already registered in the AWS Glue Data Catalog.</li><li><strong>New Pipeline:</strong> The new pipeline reads from these Iceberg tables and registers a new table with a few key considerations:</li></ol><ul><li>The job runs in <em>Oranges</em>’ AWS account (any region).</li><li>Data is written to a shared S3 bucket set up in <em>Pears</em>’ AWS account in the us-east-1 region.</li></ul><h3>Pros and Cons of the Approach</h3><p><strong>Pros:</strong></p><ul><li><strong>Familiar Mechanism:</strong> This approach extends the existing setup with minimal changes, leveraging the current infrastructure.</li><li><strong>Consistent Data Format:</strong> Using Apache Iceberg ensures that the data format remains consistent and optimized for large-scale queries.</li><li><strong>No New Services Required:</strong> There’s no need to onboard a new service or tool, which keeps maintenance and complexity low.</li><li><strong>Incremental Processing &amp; Testing:</strong> The code is written in Spark SQL and managed by dbt, which provides built-in mechanisms for incremental processing and testing.</li></ul><p><strong>Cons:</strong></p><ul><li><strong>Complex Session Configuration:</strong> Setting up Glue and Spark session configurations to handle cross-account, cross-region jobs can be tricky and prone to errors.</li><li><strong>Limited Documentation &amp; Support:</strong> While there is documentation available, it’s often sparse and the community support for this specific setup is still quite limited.</li></ul><blockquote>Before we get too deep into the weeds</blockquote><blockquote>Although not necessary, it would be a good idea to familiarize yourself with these topics. If nothing else, it will help <em>you</em>appreciate the saga.</blockquote><blockquote><strong>AWS Glue &amp; Athena </strong>: Basics of Glue Catalog and Jobs for data integration and ETL workflows and Athena Query editor.</blockquote><blockquote><strong>IAM &amp; Permissions</strong>: Basic understanding of AWS IAM roles and cross-region/cross-account access.</blockquote><blockquote><strong>dbt</strong>: Experience with dbt for transforming data, managing models, and using profiles.</blockquote><blockquote><strong>Iceberg &amp; S3</strong>: Familiarity with Iceberg tables, S3 data storage, and querying strategies.</blockquote><blockquote><strong>Spark</strong>: Basic knowledge of Spark jobs in AWS Glue and optimizing performance for large datasets.</blockquote><h3>The Journey: Overcoming Cross-Region Hurdles with Iceberg, Glue, and Spark</h3><p>Tackling a project like this felt like setting out on an expedition — full of unexpected twists and the thrill of discovery. With <em>Oranges</em> and <em>Pears</em> agreeing to share data across accounts and regions, we had a plan in place, but the path wasn’t as straightforward as it seemed. Here’s how the journey unfolded:</p><h4>Step 1: Iceberg’s Assumptions (And Ours)</h4><p>We started with Iceberg’s suggestion: use AssumeRoleAwsClientFactory to dynamically assume a role and switch regions as needed. On paper, it seemed perfect—just assume the right role, make sure it has access to the target bucket in <em>Pears</em>’ AWS account (us-east-1), and let the magic happen.</p><p><strong>Reality check</strong>: when we registered the Iceberg catalog using this setup, it couldn’t read from the local Glue catalog. We hit our first obstacle — region conflicts. Iceberg couldn’t play nicely when we changed the region within session.</p><p>It was like following a map only to realize the road you’re on doesn’t exist. We needed to find a way to access Glue tables across regions.</p><h4>Step 2: Navigating the AWS Glue Maze</h4><p>Next, we turned to AWS Glue’s documentation, which pointed us towards creating a <strong>data link</strong> in the target region. This link would act as a bridge, connecting us to the source region’s database. Great! We tested it out using Athena’s query editor in the target region, and it worked seamlessly.</p><p>We were optimistic — until we tried running the Glue interactive session in the source region (us-west-1) and switched regions dynamically to the target (us-east-1). Suddenly, the bridge seemed to vanish. The catalog couldn’t use the data link, throwing errors left and right. Sometimes, it couldn’t find the table; other times, it struggled to resolve the correct S3 endpoint.</p><p>It felt like we had found a hidden passage but couldn’t unlock the door. We needed a different approach.</p><h4>Step 3: The Aha Moment with Multi-Catalog Magic</h4><p>After scouring forums and reading between the lines of every error message, we pieced together a new plan. <strong>Instead of forcing one catalog to do everything, why not set up two catalogs with clear roles?</strong></p><ol><li><strong>The Default Catalog (</strong><strong>glue_catalog):</strong> Handles reading and writing data in the local region. It’s our trusty companion for familiar terrain.</li><li><strong>The Additional Catalog (</strong><strong>read_catalog):</strong> Specially configured for cross-region reads using an assumed role. This one is like our guide for navigating foreign lands.</li></ol><blockquote>In a Spark session, a <strong>catalog</strong> is a unified interface for managing and querying metadata of databases, tables, and views. It acts as a registry that tells Spark where and how to access data stored across different sources, like Glue, Hive, or Iceberg.</blockquote><p>By embracing this dual-catalog setup, we found the sweet spot between the flexibility of a PySpark notebook and the structured capabilities of dbt models. We could read from our local region, switch roles, and then write to the target region without breaking stride.</p><h4>The Payoff: A Harmonious Workflow</h4><p>This solution felt like finding the last missing piece of a puzzle. It allowed us to extend our existing dbt-glue setup without adding unnecessary complexity or services. The journey wasn’t without its frustrations, but it led us to a creative and robust solution — a fitting reminder that the path to success is rarely a straight line.</p><h3>Configuration Journey: Overcoming the Cross-Region Hurdle</h3><p>To make our cross-account, cross-region pipeline come to life, we needed a solid configuration that blended familiar tools with creative solutions. Our approach was to leverage a <strong>dual-catalog setup</strong> in dbt-glue:</p><p>We created two catalogs:</p><ul><li><strong>glue_catalog</strong>: This is our primary catalog for reading and writing data within the target region.</li><li><strong>read_catalog</strong>: As the name implies, it’s tailored for reading source data from the original region.</li></ul><p>The dbt-glue session kicks off in the <strong>target region</strong>, where the TARGET_S3_BUCKET is located.</p><p>The <strong>GLUE_SERVICE_ROLE</strong> handles the heavy lifting with permissions across both regions:</p><ul><li>It must be able to assume roles, making it crucial to grant it permissions explicitly, even on itself.</li><li>The role needs full access to discover and manage Glue tables, plus S3 permissions to handle operations on the shared <em>Pears’</em>s S3 bucket.</li></ul><p>With these fundamentals in place, here’s how we defined the project configuration:</p><pre>cross-region-project:<br>  outputs:<br>    test-target:<br>      type: glue<br>      role_arn: &quot;{{ env_var(‘GLUE_SERVICE_ROLE’) }}&quot;<br>      region: &lt;target_region&gt;<br>      glue_version: &quot;4.0&quot;<br>      workers: 10<br>      worker_type: G.2X<br>      idle_timeout: 10<br>      schema:  &quot;{{ env_var(‘SCHEMA_NAME’) }}&quot;<br>      session_provisioning_timeout_in_seconds: 900<br>      location: &quot;{{ env_var(‘TARGET_S3_BUCKET’) }}&quot;<br>      datalake_formats: iceberg<br>      default_arguments: &quot;--enable-auto-scaling=true,  <br>                          --enable-metrics=true, <br>                          --enable-continuous-cloudwatch-log=true, <br>                          --enable-continuous-log-filter=true, <br>                          --enable-spark-ui=true&quot;<br>      conf: &quot;spark.sql.legacy.allowNonEmptyLocationInCTAS=true<br>            --conf spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions <br>            --conf spark.serializer=org.apache.spark.serializer.KryoSerializer <br>            --conf spark.sql.warehouse={{ env_var(‘TARGET_S3_BUCKET’ )}}        <br>            --conf spark.sql.catalog.glue_catalog=org.apache.iceberg.spark.SparkCatalog <br>            --conf spark.sql.catalog.glue_catalog.catalog-impl=org.apache.iceberg.aws.glue.GlueCatalog <br>            --conf spark.sql.catalog.glue_catalog.io-impl=org.apache.iceberg.aws.s3.S3FileIO <br>            --conf spark.sql.catalog.glue_catalog.lock-impl=org.apache.iceberg.aws.dynamodb.DynamoDbLockManager <br>            --conf spark.sql.catalog.glue_catalog.lock.table=myGlueLockTable<br>            --conf spark.sql.catalog.glue_catalog.warehouse={{ env_var(‘TARGET_S3_BUCKET’) }}<br>            --conf spark.sql.catalog.read_catalog=org.apache.iceberg.spark.SparkCatalog <br>            --conf spark.sql.catalog.read_catalog.catalog-impl=org.apache.iceberg.aws.glue.GlueCatalog<br>            --conf spark.sql.catalog.read_catalog.io-impl=org.apache.iceberg.aws.s3.S3FileIO <br>            --conf spark.sql.catalog.read_catalog.lock-impl=org.apache.iceberg.aws.dynamodb.DynamoDbLockManager <br>            --conf spark.sql.catalog.read_catalog.lock.table=myGlueLockTable<br>            --conf spark.sql.catalog.defaultCatalog=glue_catalog<br>            --conf spark.sql.catalog.read_catalog.client.factory=org.apache.iceberg.aws.AssumeRoleAwsClientFactory<br>            --conf spark.sql.catalog.read_catalog.client.assume-role.arn={{ env_var(‘GLUE_SERVICE_ROLE’)}}<br>            --conf spark.sql.catalog.read_catalog.client.assume-role.region=us-west-1<br>            --conf spark.sql.catalog.read_catalog.warehouse={{ env_var(‘TARGET_S3_BUCKET’) }}&quot;<br>      tags: &quot;application=app_name, owner=DE, env=TEST&quot;</pre><h3>Putting It All Together: Sample Model</h3><p>The heart of our implementation is a simple but powerful dbt model. We read source data using read_catalog and merge it into the target table using glue_catalog.</p><p>Here’s a sample model:</p><pre>{{ config(<br>    materialized=&#39;incremental&#39;,<br>    incremental_strategy=&#39;merge&#39;,<br>    unique_key=&#39;sales_id&#39;<br>) }}<br><br>WITH source_data AS (<br>    SELECT<br>        sales_id,<br>        customer_id,<br>        product_id,<br>        sales_date,<br>        sales_amount<br>    FROM read_catalog.source_database.sales_data<br>    WHERE sales_date &gt; (SELECT MAX(sales_date) FROM glue_catalog.target_database.sales_data)<br>),<br><br>final_data AS (<br>    SELECT<br>        sales_id,<br>        customer_id,<br>        product_id,<br>        sales_date,<br>        sales_amount<br>    FROM source_data<br>)<br><br>SELECT * FROM final_data</pre><p>In this model:</p><ul><li><strong>read_catalog</strong> reads from the source data in us-west-1.</li><li><strong>glue_catalog</strong> retrieves the watermark for incremental data processing.</li><li>dbt automatically uses glue_catalog for merging the data into the target table in the us-east-1 region.</li></ul><h3>Tips for a Smooth Ride</h3><ol><li><strong>Permission Setup</strong>: Double-check IAM role permissions for seamless cross-account S3 access, Glue catalog operations, and STS role assumptions.</li><li><strong>Session Configuration</strong>: Register your catalogs right at the start of your Spark session to avoid pesky errors about unregistered catalogs.</li><li><strong>Testing</strong>: Start with a small data subset before unleashing it on the full dataset in production.</li></ol><h3>Wrapping It Up</h3><p>Our dual-catalog strategy within dbt-glue bridges the gap between two regions and accounts with ease. By leveraging the strengths of Apache Iceberg and dbt, we’ve sidestepped the common pitfalls of default Glue configurations and crafted a scalable solution. This journey wasn’t just about moving data — it was about finding the most efficient path forward, one configuration at a time.</p><h3>Further Reading</h3><ul><li><a href="https://docs.aws.amazon.com/glue/index.html">AWS Glue Documentation</a></li><li><a href="https://iceberg.apache.org/docs/1.5.1/aws/#cross-account-and-cross-region-access">Apache Iceberg Cross-Region Cross-Accoun</a>t</li><li><a href="https://github.com/awslabs/dbt-glue">dbt-glue GitHub Repository</a></li><li><a href="https://aws.amazon.com/blogs/big-data/configure-cross-region-table-access-with-the-aws-glue-catalog-and-aws-lake-formation/">AWS Glue Cross-Region Access Guide</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2d430595b523" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ELT Using Glue DBT]]></title>
            <link>https://medium.com/@mihirsaurkar/elt-using-glue-dbt-15b4af93909b?source=rss-2c7b00e38dbd------2</link>
            <guid isPermaLink="false">https://medium.com/p/15b4af93909b</guid>
            <category><![CDATA[etl]]></category>
            <category><![CDATA[aws-glue]]></category>
            <category><![CDATA[dbt]]></category>
            <dc:creator><![CDATA[Mihirsaurkar]]></dc:creator>
            <pubDate>Mon, 29 Jan 2024 22:19:04 GMT</pubDate>
            <atom:updated>2024-01-30T00:51:43.219Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MwnFMgZtatwX3Qxf6OZvfA.png" /></figure><p>I came across the AWS big data blog that advocates using S3 as data store and manage transformations using GLUE spark interactive session, which are run using the DBT core with GLUE adapter.</p><p>Ref : <a href="https://aws.amazon.com/blogs/big-data/build-your-data-pipeline-in-your-aws-modern-data-platform-using-aws-lake-formation-aws-glue-and-dbt-core/">https://aws.amazon.com/blogs/big-data/build-your-data-pipeline-in-your-aws-modern-data-platform-using-aws-lake-formation-aws-glue-and-dbt-core/</a></p><p>The issue most Analytics team face with transforming data with warehouse are:</p><ul><li>Scalability / Resource contention between analytics workloads and ELT workloads</li><li>Limit on data formats and interoperability across different data processing engines</li><li>Performance pitfalls when querying highly nested data</li></ul><p>Most of these could be solved by migrating to snowflake but it may not always be a feasible option.</p><p>A good solution to enjoy processing big data with spark and query tables with Red Shift is using managed GLUE-Spark jobs, however consider the following pros and cons:</p><p>Pros:</p><ul><li>The scheduling is in built with GLUE workflows</li><li>Glue Studio provides an interactive visual designer for pipeline development</li></ul><p>Cons:</p><ul><li>The data lineage is not provided out of the box</li><li>Code if generated by the visual editor will be boiler plate and not optimized</li><li>The code will be PySpark so significant learning curve if teams are more well versed with SQL, (even if running logic with spark SQL operators)</li><li>Needs explicit integration to commit code to version control tools like git</li></ul><p>Another variation which provides the same power and flexibility of using Glue managed spark jobs while maintaining transformations as SQL is using DBT-Glue adapter. Its is a good half way between continuing to use SQL for developments where DBT generates the spark code around your data models to run the managed Glue Jobs. That being said it comes with its own quirks. Below are some pros and cons of this approach.</p><p>Pros:</p><ul><li>Analytics team are more familiar using DBT</li><li>DBT is flexible and can be used against several data processing engines (redshift, snowflake, mysql, postgres)</li><li>DBT is primarily SQL code, with short learning curve to pickup DBT</li><li>DBT provides ready way to generate and host DBT docs which help with schema and data-lineage documentation</li></ul><p>Cons:</p><ul><li>Does not come with scheduling unless implemented with DBT cloud</li><li>Will require orchestrator like Airflow to schedule transformation</li><li>Configuring the required IAM users and service roles is a tedious task</li></ul><p>The reference article talks about setting up the pipeline using AWS Glue run from AWS command shell. It uses lambda service to update and load data into AWS data catalog.</p><p>Setting up lambda to load and refresh data is very custom solution and will need maintenance and development efforts. A more out of box easy to configure option for development and deployment is using a crawler. Here is a jist of another end to end implementation in real world application. The use case I am tackling is more ETL then Analytics but to serve analytics at any stage:</p><ul><li>Use DMS to transport data from production database to s3 bucket</li><li>Use a scheduled crawler to register new tables and load partitions into AWS data catalog table</li><li>Use DBT-Glue with Airflow to run models and generate DBT-docs and land data into target s3 bucket</li><li>Optionally use DMS again to take data from s3 target bucket and load into Redshift.</li><li>Redshift can also integrate/query iceberg tables stored on S3 may need additional IAM configurations to allow this.</li></ul><p>Now that we have decided to use DBT with Glue for ETL we need make a few more decisions before we can start implementation journey. One of the features that make DBT so desirable is it enables incremental updates and provides different materializations as supported by the underlying database engine. This takes away the complication of managing CDC from developers.</p><p>DBT-Glue has few more knobs that we need to tweak. The config block and the profiles allow for various data-lake formats. Each has its own advantage. We need to consider each and select one as this will significantly influence the set up. By default DBT uses Spark SQL and parquet hive storage file system and incremental models work at the partition level. Implementing a suitable data-lake unlocks “merge” operations and for all practical purposes makes DBT-Glue same to any relational databases incremental model, utilizing the unique key to incrementally update models.</p><p>The data lake standards supported by DBT-Glue are as follows:</p><ul><li>Hudi</li><li>Delta Lake</li><li>Iceberg</li></ul><p>After considering each of the standards I chose to go with Iceberg.</p><p>Below are my learnings from setting up DBT to store tables as iceberg, and simulating a continuous updating data pipeline. I have tried to document configurations that are easy to miss and templates that worked for me.</p><p>Beginning DBT project starts with getting credentials to the underlying database. With DBT-Glue, glue managed spark jobs in form of interactive sessions serves as the database engine. Thus we need to start with setting up IAM.</p><p>To be able to run DBT glue jobs, DBT should be able to perform following actions:</p><ol><li>Have associated IAM Glue Role: role authenticate with AWS account</li><li>IAM Glue Role is able to create, destroy and communicate with AWS Glue components to manage job life cycle</li><li>The role should have assume permission on the AWS Glue Role</li><li>The glue role suggested for this use case is the AWS managed AWSGlueServiceRole</li><li>The IAM user should have trust relationship to allow assume role for the AWSGlueServiceRole</li><li>When DBT executes the assume role, it establishes an assume role session which has its own secrets , session token and session name.</li><li>This session should also have assume role permissions on the AWSGlueServiceRole.</li><li>To the AWSGlueServiceRole, do not forget to add any other additional permissions like Dynamo access to create and destroy tables if using the Iceberg lake.</li></ol><p>Below is sample trust relationship :</p><pre>{<br>  &quot;Version&quot;: &quot;2012-10-17&quot;<br>  &quot;Statement&quot;: [ <br>          {<br>            &quot;Effect&quot;: &quot;Allow&quot;,<br>            &quot;Principal&quot;: {<br>              &quot;Service&quot;: &quot;glue.amazonaws.com&quot; <br>            },<br>            &quot;Action&quot;: &quot;sts:AssumeRole&quot;<br>         },<br>         {<br>            &quot;Effect&quot;: &quot;Allow&quot;,<br>            &quot;Principal&quot;: {<br>              &quot;AWS&quot;: &quot;&lt;GLUE-USER-ARN&gt;&quot; <br>            },<br>            &quot;Action&quot;: &quot;sts:AssumeRole&quot;<br>         },<br>         {<br>            &quot;Effect&quot;: &quot;Allow&quot;,<br>            &quot;Principal&quot;: {<br>              &quot;AWS&quot;: &quot;arn:aws:sts::&lt;account_id&gt;:assumed-role/AWSGLueServiceRole/&lt;session_name&quot; <br>            },<br>            &quot;Action&quot;: &quot;sts:AssumeRole&quot;<br>         }<br>]<br>}</pre><p>Next we need to set up our DBT project to correctly use the IAM user and roles.</p><pre>   dev_glue:<br>      type: glue<br>      role_arn: arn:aws:iam::000000000:role/GlueRoleName<br>      schema: &quot;my_schema&quot;<br>      location: &quot;s3://my-s3-bucket/prefix/my_schema&quot;<br>      iceberg_glue_commit_lock_table: DbtGlueLockTable<br>      region: &lt;aws-region&gt;<br>      workers: 8<br>      worker_type: G.1X<br>      glue_version: &quot;3.0&quot;<br>      idle_timeout: 15<br>      session_provisioning_timeout_in_seconds: 300<br>      connections: iceberg_connector_name<br>      conf: &quot;--conf spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions<br>             --conf spark.serializer=org.apache.spark.serializer.JavaSerializer<br>             --conf spark.sql.catalog.glue_catalog.warehouse=s3://s3://my-s3-bucket/prefix/my_schema<br>             --conf spark.sql.catalog.glue_catalog=org.apache.iceberg.spark.SparkSessionCatalog<br>             --conf spark.sql.catalog.glue_catalog.catalog-impl=org.apache.iceberg.aws.glue.GlueCatalog<br>             --conf spark.sql.catalog.glue_catalog.io-impl=org.apache.iceberg.aws.s3.S3FileIO<br>             --conf spark.sql.catalog.glue_catalog.lock-impl=org.apache.iceberg.aws.glue.DynamoLockManager<br>             --conf spark.sql.catalog.glue_catalog.lock.table=DbtGlueLockTable<br>             --conf spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions<br>             --conf spark.sql.iceberg.handle-timestamp-without-timezone=true<br>             --conf spark.hive.default.fileformat=Parquet&quot;<br>      default_arguments: &quot;--enable-metrics=true, --enable-continuous-cloudwatch-log=true, --enable-continuous-log-filter=true, --enable-spark-ui=true, --spark-event-logs-path=s3://my-s3-bucket/prefix/sparklogs/&quot;</pre><p>The trouble I had was when I ran my model, dbt created a temporary table in default database. Since I am stickler for quality, I was able to catch that the serde assigned to this table was not compatible with the format. Although the file was parquet, it was not written to S3 with parquet extension. As a result AWS DataCatalog assigned the LazySimpleSerde to it. This results in data mis-allignment. After lot of research and trial and error I was able to come up with the above configs.</p><p><strong>NOTE:</strong></p><ul><li><strong>before running model ensure both source and destination database are defined in AWS Glue Databases.</strong></li><li><strong>The schema parameter associates with AWS databases</strong></li></ul><p>For more detailed explanation on setting up the DBT profiles for Glue visit DBT documentation: <a href="https://docs.getdbt.com/docs/core/connect-data-platform/glue-setup"><strong>DBT Docs</strong></a></p><p>Now that our project is set up we can run models. One of the common errors with iceberg table with DBT Glue is trouble referencing an existing table. To overcome this add the “glue_catalog” prefix before reference tables.</p><p>Declaring tables to be saved as iceberg use the following config block in the models:</p><pre>{{ config(<br>    materialized=&#39;incremental&#39;,<br>    incremental_strategy=&#39;merge&#39;,<br>    unique_key=[&quot;customer_id&quot;],<br>    file_format=&#39;iceberg&#39;,<br>    partition_by=[&#39;dt&#39;]<br>) }}<br><br>select * from  glue_catalog.{{ ref(&#39;my_other_table&#39;)}}</pre><p>Setting up ELT with DBT-Glue can be both exciting and challenging. While DBT Docs offer valuable guidance, the devil lies in the detail. I have tried my best to outline my journey with DBT-Glue and some solutions that worked. Hope this provides you with some direction to start exploring!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=15b4af93909b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Migration of a Workload running in a Corporate Data Center to AWS using the Amazon EC2 and RDS…]]></title>
            <link>https://medium.com/@mihirsaurkar/migration-of-a-workload-running-in-a-corporate-data-center-to-aws-using-the-amazon-ec2-and-rds-5967d7942192?source=rss-2c7b00e38dbd------2</link>
            <guid isPermaLink="false">https://medium.com/p/5967d7942192</guid>
            <dc:creator><![CDATA[Mihirsaurkar]]></dc:creator>
            <pubDate>Mon, 05 Jun 2023 20:51:07 GMT</pubDate>
            <atom:updated>2023-06-05T20:51:07.652Z</atom:updated>
            <content:encoded><![CDATA[<h3><em>Migration of a Workload running in a Corporate Data Center to AWS using the Amazon EC2 and RDS service</em></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MYrkt1lvCnZ1olMs3npi1w.png" /></figure><p>In another project based in a real-world scenario, I acted as the Cloud Specialist responsible for migrating a workload running in a Corporate DataCenter to AWS.<br> The application and database were migrated to AWS using the Lift &amp; Shift (rehost) model, moving both application and database data.</p><p>I followed some migration steps: Planning (sizing, prerequisites, resource naming), Execution (resource provisioning, best practices), Go-live (validation test — Dry-run, final migration — Cutover) and Post Go-live (ensure the operation of the application and user access).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eNG_WeKlFzl-NtZAeOTkTA.jpeg" /></figure><h3>Plan</h3><p>During planning phase account for the on premise components and note down the following:</p><ul><li>Map the capacity being used to understand the cloud infrastructure to be provisioned.</li><li>This includes number of nodes of web servers/databases, versions of OS and database being run CPU memory and total storage being utilized.</li><li>Also note down the pre-requisites of running the application packages that should be installed and their dependencies.</li><li>Maintain a 1–1 mapping of on premise components to the ones that will be created in AWS</li></ul><h3>Implementation</h3><p>This is the actual execution of the planned infrastructure.</p><ul><li>Create the instances as planned in AWS.</li><li>Apply best practices as suggested by AWS and industry practices to design the architecture.</li><li>Implement security and safe guard against un-authorized access.</li></ul><h3>Go Live</h3><ul><li>Dry run the application with test loads</li><li>Perform all the workflows the application shoul support</li><li>Identify any bugs or gaps early on.</li><li>Cut over from on premise to cloud by shutting down the on premise, transferring data to cloud and redirecting load to cloud infrastructure.</li></ul><h3>Post Go Live Support:</h3><ul><li>Monitor the solution closely to check work loads verify capacity is sufficient and there are no bottlenecks</li><li>Respond rapidly to any production failures to reduce down time.</li></ul><h4>After Planning phase the identified architecture is as follows:</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0Y_jNDDOvBs6UwkMYSFa2g.png" /></figure><p>As part of this project I will be creating the following AWS Components:</p><h4>Network:</h4><ul><li>A <strong>Virtual Private Cloud (VPC)</strong> is a virtual network infrastructure provided by AWS. It offers a logically isolated section of the cloud where users can launch resources such as virtual machines, databases, and storage instances.</li><li>The VPC is divided into multiple subnets.</li><li>A <strong>public subnet</strong> is configured with a routing table that includes a route to the internet gateway, allowing outbound traffic from instances in the subnet to reach the internet and inbound traffic from the internet to reach instances in the subnet.</li><li>A <strong>private subnet</strong> is configured with a routing table that does not include a route to the internet gateway. This prevents instances within the private subnet from establishing direct connections to the internet.</li><li>An <strong>internet gateway</strong> is a horizontally scalable, redundant, and highly available component in Amazon Web Services (AWS) that enables communication between instances within a Virtual Private Cloud (VPC) and the internet. It serves as a connection point between the VPC and the public internet, allowing instances in the VPC to access the internet and be accessed from the internet.</li></ul><h4>RDS:</h4><ul><li>Data base instance running MySQL</li></ul><h4>EC2:</h4><ul><li>Ubuntu instance to run the web application</li></ul><h3>Implementation</h3><h4>VPC</h4><p>On AWS console navigate to VPC service and start the process by clicking the create VPC. I am going to create all the components separately so proceeding with VPC only option. I provided a name and IPv4 CIDR block.</p><p>Considerations for CIDR block:</p><ul><li>It should not overlap with on premise network</li><li>It should not overlap with other cloud infrastructure</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PQF-y3jqLCIFfGzHPMmGag.png" /></figure><p>As per the architecture the web server needs to be in public subnet such that it can access internet and users can access it. The database server should be within the private subnets and should only be accesses by application server. I will need an Internet Gateway to allow access to internet.</p><p>I am keeping this project open to internet since this is not an actual production application. In production application it would be prudent to great pre verified list of IP addresses expected to access the application and accordingly creating security groups.</p><h4>Subnets:</h4><p>Navigate to VPC and select subnets. I selected the previously created VPC to create subnet under it.</p><p>I name this as public subnet and give ti a CIDR block. 10.0.0.0/24</p><p>I selected the availability zone as the first on the list, this will be dictated by the application and its availability constrains if any.</p><p>Then I click create subnet and public subnet is ready.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*O-awWA-S7Gz1r3-YA5YqkA.png" /></figure><p>I repeat the process to create a 2 private subnets by providing appropriate names.</p><p>Selecting the previously created VPC and assigning the CIDR blocks to ensure there is no overlap as follow :</p><ul><li>10.0.1.0/24</li><li>10.0.2.0/24.</li></ul><blockquote>The optional step here is to add appropriate tags to the infrastructure. It is a good practice to tag resources as more applications are deployed and the infrastructure stack starts to grow it is difficult to remember where they are used and for what.</blockquote><blockquote>Tags can be handy to keep track of components and the associated costs.</blockquote><p>Below are the subnets I created:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lUsupBWw5IDYqSBBLc8U4g.png" /></figure><h3>Internet Gateway</h3><p>Select Internet Gateway from the VPC menu and select create internet gateway.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*v1UxS7gwlzOPYpMsnzfuIw.png" /></figure><p>I provided a suitable name and click on create internet gateway.</p><p>Once the Internet Gateway is created navigate to the dashboard and locate the newly created gateway . Select the gateway and click on action and select attach to VPC.</p><p>Once I have the VPC, subnets and Internet Gateway created now to update the route table.</p><h3>Route Table</h3><p>Navigate to Route Table on left tab of VPC and select the route table attached to the VPC created previously.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dHUIjid9HO69Am3sobv2Fg.png" /></figure><p>Then click edit routes</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*z0UnAFHmgrMIfwihfIPHKg.png" /></figure><p>The local will be pre populated and I added the access to internet as below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Zsve91-gMmzU8G2Zc61CFg.png" /></figure><p>The ip 0.0.0.0/0 indicates traffic from anywhere will be routed through internet gateway.</p><p>This concludes the network set up.</p><h3>Web Server EC2</h3><p>Navigate to the EC2 service and select launch an instance</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CdWCFkovsoAXbcXy2Pml7g.png" /></figure><p>Provide the name for the instance and select the image, here I am going with ubuntu this will again be dictated by the on premise environment.</p><p>Next is to select the instance type this depends on the capacity sizing exercise done in planning phase. The goal is to select an instance with at least as much capacity (CPU, memory and storage) as the on premise server.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4KoSEcdp4iHM7FSAm6hUEQ.png" /></figure><p>I created a key pair for accessing this instance and downloaded the key to perform the next steps of set up.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/966/1*kMF06cFm0ihnpic6o7wDMA.png" /></figure><p>Now I define the network settings by clicking the edit. Select the previously created VPC and assign it the public subnet. I enabled public IP and created a new security group. This allows the EC2 to be connected with SSH from anywhere. Since this is not recommended, I am going to restrict this access to my IP only.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zQqMwaVbRGd9mAaLrFTfXQ.png" /></figure><p>Now add another rule to open up port 8080 to the internet to be able to access the application.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ewcZqzmBuGh5XgzJPg9Qpg.png" /></figure><h3>AWS RDS</h3><p>Navigate to RDS service and click create Database and select standard create.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2TMRiaAt_F77OUbe8bqLzA.png" /></figure><p>I will database with MySQL engine and select the version, here I try to select the same version as the on premise data base. I want to keep the same versions so as to not introduce unwanted errors. Upgrades can be taken up as separate projects later once migration is successfull.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*E4UvOUB2OplYtlapR_UJIw.png" /></figure><p>I am going to use the free tier for this exercise:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JwVlyK1qO_jba5NHWPmPcA.png" /></figure><p>I give the database instance a suitable name and create the root user and set the password</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vqJ2KXJaoZ5iSAWEvcn4sg.png" /></figure><p>Then I select the capacity, this is defined in the planning phase based on the capacity of the on premise database.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Q22UC5Y7v2oXyT3PMmTXOw.png" /></figure><p>Now I set up the network configuration, the database does not need to connect to compute resource. I choose the previously created VPC and I do not want the database to be accessible from the internet.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*c-9GT3lBc0q-J3Vq72xZlQ.png" /></figure><p>I will create a new security group and give an appropriate name and choose the correct availability zone this should be decided during the planning phase.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QzcfURjFvd-IbBJ-iw-pPw.png" /></figure><p>I set the authentication to be password authentication</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yXzX4z9mX6Q8BpHMDnFcHg.png" /></figure><p>Re-configure the security group to allow access to database from EC2 instance:</p><ul><li>Go to the VPC service on console</li><li>Select the security groups in the security sub sections</li><li>Select the security group created for database instance by the name given at creation time.</li><li>It should have singe IP in the in bound rules</li><li>Edit the inbound rules and add another and choose the Private IP of the EC2 instance by navigating to the EC2 instance and copying the private IP.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uITSX69PK-JLzldLxBxt_g.png" /></figure><p>The database will now accept connection requests only from the EC2 instance.</p><h3>Installing Pre-requisite on EC2</h3><p>During the planning phase software versions and packages are identified that need to be installed on EC2 instance.</p><p>I will connect to the EC2 and carry out the installations:</p><ul><li>Open terminal on local machine</li><li>Navigate to the folder containing the private key which was downloaded while creating the EC2 instance</li><li>Run the below command highlighted in example below</li></ul><figure><img alt="Navigate to EC2 instance on AWS console and select the connect tab then ssh the command is available to copy" src="https://cdn-images-1.medium.com/max/1024/1*r8_mN_NJs7s_nizR952yUg.png" /></figure><ul><li>Once connected to the EC2 instance install pre requisites by executing unix commands</li></ul><h3>Uploading Data Dump to Database</h3><p>I am going to do this by remotely connecting to EC2 instance and then logging into the database instance with root user.</p><ul><li>Follow steps as mentioned before to connect to EC2 instance</li><li>Once connected to EC2 login to MySQL using the command</li></ul><pre>mysql -h &lt;hostname&gt; -P 3306 -u admin -p </pre><ul><li>When prompted enter root password for admin</li><li>Once connected lets create the data base and application user by running the following</li></ul><pre>CREATE DATABASE database_name;<br><br>CREATE USER user_name IDENTIFIED BY &#39;password&#39;;<br><br>GRANT ALL PREVILEGES ON database_name;<br><br>FLUSH PREVILEGES;</pre><p>The database is now ready to be used by application</p><h3>Starting the Application on EC2</h3><ul><li>Again I remotely connect to EC2 instance using the private key and command from earlier.</li><li>Upload the zipped application and the data base dump to the EC2 instance.</li><li>The files can be uploaded to a S3 bucket and then downloaded to EC2 instance using AWS cli</li><li>I unzipped the application folder and updated the configuration by inserting the database instance name.</li><li>I logged back into the MySql instance using the previous commands</li></ul><pre>mysql -h &lt;hostname&gt; -P 3306 -u admin -p</pre><ul><li>I run the below command to update the tables from dump</li></ul><pre>USE database_name;<br>SOURCE mysql_dump.sql;</pre><ul><li>Exit the mysql client</li><li>Start the application running the entry point.</li></ul><p>Once that is done the application is running and is available to be accessed on the internet.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XmeRcT6dexTDWwhcElD8Kg.png" /></figure><p>Home page</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*I_DlPXHMtzGMTw4iSQqARA.png" /></figure><p>Existing articles as loaded into database.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uVGnzeobbaViv_gTF8aCGQ.png" /></figure><p>Next steps in the migration are testing the workflows and cutting over, by closing down the on premise application and directing traffic to cloud application. Since this was just a simulation this concludes the excercise as I have demonstrated how an application is migrated from on premise to cloud infrastructure.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5967d7942192" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[From Theory to Practice: Implementing Multi-class Classification with KNN SKLearn]]></title>
            <link>https://medium.com/@mihirsaurkar/from-theory-to-practice-implementing-multi-class-classification-with-knn-sklearn-2d85f9adc5f7?source=rss-2c7b00e38dbd------2</link>
            <guid isPermaLink="false">https://medium.com/p/2d85f9adc5f7</guid>
            <category><![CDATA[k-nearest-neighbours]]></category>
            <category><![CDATA[text-mining]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[sklearn]]></category>
            <dc:creator><![CDATA[Mihirsaurkar]]></dc:creator>
            <pubDate>Thu, 25 May 2023 21:00:12 GMT</pubDate>
            <atom:updated>2023-05-25T21:00:12.487Z</atom:updated>
            <content:encoded><![CDATA[<p>I recently came across a very common problem in managing data which is normalization. Most analysts would prefer to go about it on excel by find and replace. It was on my bucket list to try and solve the problem for large data sets that exceed excels capacity. I had employed OpenRefine, a tool that provides a GUI and ability to select different levels of strictness to create similarity clusters and then impute a class value to those clusters. However this approach is mundane, laborious and down right boring, thus subject to human errors.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/686/1*T2lnf_Xm9Dey62bqLU0Z4Q.png" /></figure><p>I experimented with creating KNN classifier and will contrast it with Neural Network using Tensorflow in my next article.</p><h3>Problem Statement</h3><p>Given data set of misspelt , jumbled or missing characters decipher the normalized name to curate the data quality.</p><h3>Data Set</h3><p>To simulate this exercise I chose retailer data set, by down loading a list of top 100 retailers in US. I created a bunch of methods to impute random characters and numbers. To keep it really vague I also randomized the number of imputations to a singe string, just to ensure the data does not get impossible to comprehend I limited number of imputation between 0 to 50% of length of String.</p><pre><br>import string<br>import random <br>import numpy as np <br>import pandas as pd<br><br># Using seen hash table to generate all unique values<br># Main goal here is to ensure there is no cross category contamination<br>seen = {}<br><br>def gen_impurities(n,word):<br>    for i in range(n):<br>        index = random.randint(0,len(word)-1)<br><br>        if index%2 == 0:<br>            symbol = random.choice(string.punctuation)<br>            word = word[:index] + symbol + word[index:]<br><br>        if index%7 == 0:<br>            num = random.choice(string.digits)<br>            #print(f&quot;Number = {num}&quot;)<br>            word = word[:index] + str(num) + word[index:]<br><br>        if index%3 == 0:<br>            word.replace(word[index],&quot;&quot;)<br>    return word<br><br>def obfuscate(word):<br>    n = int(len(word)*0.5)<br>    iteration = random.randint(1,n)<br>    label = word<br>    for i in range(5):<br>        imputed_word = gen_impurities(iteration,label)<br>        if imputed_word not in seen:<br>            seen[imputed_word] = 1<br>            return imputed_word<br>        seen[imputed_word] = seen[imputed_word]+1<br>    return word<br><br>def gen_gibberish(min_l,max_l):<br>    l = random.randint(min_l,max_l)<br>    res = &#39;&#39;.join(random.choices(string.ascii_uppercase +<br>                                 string.punctuation +<br>                                 string.digits, k=l))<br>    while res not in seen:    <br>        seen[res] = 1<br>        return res<br>    seen[res] = seen[res]+1<br><br>def get_obfuscated_targets(targets,n):<br>    obfuscate_target = targets * n<br>    obfuscate_targets.sort()<br>    df_obfuscated = pd.DataFrame({&quot;targets&quot;: obfuscate_targets})<br>    df_obfuscated[&#39;bad_names&#39;] = df_obfuscated[&quot;targets&quot;].apply(lambda x: obfuscate(x) )<br>    return df_obfuscated<br>    <br>def get_catch_gibberish(instance_count,min_l,max_l):<br>    others = [&#39;Other&#39;] * instance_count<br>    df_other = pd.DataFrame({&#39;targets&#39;:others})<br>    df_other[&#39;bad_names&#39;] = df_other[&#39;targets&#39;].apply(lambda x : gen_gibberish(min_l,max_l)) <br>    return df_other<br><br>def get_good_names(targets,n):<br>    good_captures = targets*  n<br>    df_good_captures = pd.DataFrame({&#39;targets&#39;:good_captures})<br>    df_good_captures[&#39;bad_names&#39;] = df_good_captures[&quot;targets&quot;]<br>    return df_good_captures<br><br>def get_data(impute_n,instance,min_l,max_l): <br>    n = impute_n<br>    instance_count = instance<br>    min_l = min_l<br>    max_l = max_l<br><br>    df_obfuscated = get_obfuscated_targets(targets,n)<br>    df_other = get_catch_gibberish(instance_count,min_l,max_l)<br>    df_good_captures = repeat_good_names(targets,n,instance_count)<br>    df = pd.concat([df_obfuscated,df_good_captures,df_other])<br>    return df</pre><h3>K-Nearest Neighbor</h3><p>The K-Nearest Neighbors (KNN) algorithm is a popular and intuitive classification and regression algorithm in machine learning. It is a non-parametric algorithm that works based on the principle of similarity. The KNN algorithm classifies or predicts a new data point by comparing it to the labeled data points in the training dataset. The algorithm measures the distance between the new data point and its K nearest neighbors in the feature space. The K nearest neighbors are determined based on a distance metric, commonly the Euclidean distance. Once the neighbors are identified, the algorithm assigns the majority class label for classification or calculates the average value for regression. KNN is a simple yet powerful algorithm that can be used for various tasks, such as recommendation systems, image classification, and anomaly detection. However, it’s important to choose an appropriate value for K and consider the impact of feature scaling to achieve optimal results with the KNN algorithm.</p><h4>Classes</h4><p>In this particular use case there are 100 classes + 1 class of gibberish strings or garbage class to ensure some random string does not classified as a valid class. I followed the standard practice of splitting the data set in test and train using 80:20 split</p><pre>def read_data():<br>    df = pd.read_csv(&quot;Targets.csv&quot;)<br>    del df[&#39;empty&#39;]<br>    df = df.set_index(&#39;no&#39;)<br>    df.head()<br>    return df<br><br><br>def get_train_test_data(split_ratio,impute_ratio,instance,min_l,max_l):<br>    test_size = (instance*split_ratio)<br>    train_size = (instance*(1-split_ratio))<br>    print(f&quot;test_size = {test_size} &amp; train_size = {train_size} &amp; split = {split_ratio}&quot;)<br>    test_impute_n = int(test_size*impute_ratio)<br>    test_good_n = int(test_size - test_impute_n)<br>    print(f&quot;test impute = {test_impute_n} &amp; instances = {test_good_n}&quot;)<br>    df = read_data()<br>    targets = list(df[&quot;target&quot;])<br>    # get imputed<br>    df_obfuscated = get_obfuscated_targets(targets,test_impute_n)<br>    # get good names<br>    df_good_names = get_good_names(targets, test_good_n)<br>    # get others <br>    df_other = get_catch_gibberish(test_impute_n+test_good_n,min_l,max_l)<br>    df_test = pd.concat([df_obfuscated,df_good_names,df_other])<br>    <br>    del df_obfuscated,df_good_names,df_other<br>    train_impute_n = int(train_size*impute_ratio)<br>    train_good_n = int(train_size - train_impute_n)<br>    # get imputed<br>    df_obfuscated = get_obfuscated_targets(targets,train_impute_n)<br>    # get good names<br>    df_good_names = get_good_names(targets, train_good_n)<br>    # get others <br>    df_other = get_catch_gibberish(train_impute_n+train_good_n,min_l,max_l)<br>    <br>    df_train = pd.concat([df_obfuscated,df_good_names,df_other])<br>    <br>    del df_obfuscated,df_good_names,df_other<br>    return df_train, df_test<br><br>def get_train_test_split(df_train,df_test,one_hot_encode_labels=False):<br>    X_train = df_train[&quot;bad_names&quot;].values<br>    y_train = df_train[&quot;targets&quot;].values<br>    X_test  = df_test[&quot;bad_names&quot;].values<br>    y_test  = df_test[&quot;targets&quot;].values<br>    <br>    if one_hot_encode_labels:<br>        df_labels = pd.concat([df_train[&#39;targets&#39;],df_test[&#39;targets&#39;]])<br>        print(f&quot;Labels = {df_labels.shape})&quot;)<br>        lables = pd.get_dummies(df_labels)<br>        lookup = list(lables.columns)<br>        print(len(lookup))<br>        del df_labels<br>        y_test_labels = pd.get_dummies(y_test)<br>        y_test_encoded = y_test_labels.astype(&#39;float32&#39;).values<br>        y_train_labels = pd.get_dummies(y_train)<br>        y_train_encoded = y_train_labels.astype(&#39;float32&#39;).values<br>        return X_train,y_train_encoded,X_test,y_test_encoded, lookup<br>    else:<br>        return X_train, y_train, X_test, y_test, None<br><br>split_ratio = 0.2<br>impute_ratio = 0.5<br>instances = 50<br>min_l = 5<br>max_l = 20<br><br>df_train, df_test = get_train_test_data(split_ratio,<br>                                        impute_ratio,<br>                                        instances,<br>                                        min_l,<br>                                        max_l)<br><br>X_train, y_train,X_test, y_test, lable_lookup = get_train_test_split(<br>                                                df_train,<br>                                                df_test,<br>                                                one_hot_encode_labels=False)</pre><h4>Text Vectorization:</h4><p>Here I chose to use Tfidf vectorizer form scikit learn by assuming combination of two characters might gives a better relationship to predicting the targets. The TfidfVectorizer is a text feature extraction method that converts a collection of text documents into a numerical representation. TF-IDF stands for Term Frequency-Inverse Document Frequency, which is a commonly used statistical measure in information retrieval and text mining.</p><p>I fit the vectorizer to my corpus of bad names which generates n-grams for length 2 (2-grams). I then transformt each observation by applying the vectorized to get transformed array.</p><pre>from sklearn.feature_extraction.text import TfidfVectorizer<br>Text = &quot;&quot;.join(X_train)<br>vectorizer = TfidfVectorizer(analyzer=&#39;char&#39;,strip_accents =&quot;ascii&quot; , ngram_range=(2,2)).fit(Text)<br><br>X_train_vector = vectorizer.transform(X_train).toarray()<br>X_test_vector = vectorizer.transform(X_test).toarray()</pre><h4>K-NN Classifier</h4><p>It’s important to note that the KNN algorithm does not involve model training as it is an instance-based learning algorithm.Below is the snippet used to create the classifier.</p><pre>from sklearn.neighbors import KNeighborsClassifier<br>from scipy.spatial import distance      <br><br>classifier = KNeighborsClassifier(<br>                  n_neighbors=20,<br>                  n_jobs=10, <br>                  metric = distance.correlation)<br><br>classifier.fit(X_train_vector, y_train)</pre><p>Here’s a breakdown of how the classifier would work with the given parameters:</p><ol><li>n_neighbors=20: This parameter determines the number of neighbors considered for classification. In this case, the classifier will consider the 20 nearest neighbors to the query point.</li><li>n_jobs=10: This parameter specifies the number of parallel jobs to run during the neighbor search. It allows for parallel processing to speed up the computation. In this case, the classifier will utilize 10 parallel jobs for the neighbor search.</li><li>metric=distance.correlation: This parameter defines the distance metric used to calculate the distance between data points. The distance.correlation metric represents the correlation distance, which measures the dissimilarity between two samples based on their correlation coefficient. It is a valid metric for KNN algorithms.</li></ol><p>Given a new data point, the classifier would perform the following steps:</p><ol><li>Calculate distances: The classifier computes the correlation distance between the new data point and all other data points in the training set.</li><li>Select neighbors: It selects the 20 nearest neighbors based on the calculated correlation distances.</li><li>Classify the data point: For classification, the majority class label among the selected neighbors is assigned as the predicted label for the new data point.</li></ol><p>The classification decision is made based on the labeled data points in the training set. The specified parameters in the KNeighborsClassifier instance influence the number of neighbors considered, parallel processing, and the distance metric used.</p><h4>Getting Predictions</h4><p>As mentioned above there is no training involved and most of the heavy lifting is done on the prediction side. The classifier essentially needs all the training instances to make a decision</p><pre>def my_classifier(w:list,vectorizer,classifier):<br>    w_vector = vectorizer.transform(w).toarray()<br>    pred = classifier.predict(w_vector)<br>    return pred</pre><p>The data I used was of top retailers the predictor functions is able to predict as below:</p><pre>test_stores = [&quot;Targe1&quot;, <br>                &quot;Larget&quot;, <br>                &quot;!a7g3T&quot;,<br>                &quot;&#39;7argay&#39;&quot;,<br>                &quot;7- 11&quot;,<br>                &quot;Ac3 Hw&quot;]<br>my_classifier(test_stores<br>              ,vectorizer,classifier)</pre><pre>OP : array([&#39;Target&#39;, &#39;Target&#39;, &#39;Other&#39;, &#39;Gap&#39;, &#39;7-Eleven&#39;, &#39;Ace Hardware&#39;],<br>      dtype=object)</pre><p>As can be seen above the model does a good job of assigning most relevant store name.</p><h4>Evaluating Model</h4><pre>from sklearn.metrics import classification_report<br>from sklearn.metrics import confusion_matrix<br>from sklearn.metrics import accuracy_score<br><br>conf_mat = pd.DataFrame({&#39;test_names&#39;:X_test,&#39;actual&#39;:y_test})<br>conf_mat[&quot;predicted&quot;] = conf_mat[&quot;test_names&quot;].apply(<br>                                          lambda x : my_classifier([x],<br>                                                              vectorizer,<br>                                                              classifier)[0])<br><br><br><br>result = accuracy_score(<br>                  conf_mat[&#39;actual&#39;].values,<br>                  conf_mat[&#39;predicted&#39;].values)<br>print(&quot;Accuracy:&quot;,result) </pre><pre>OP: Accuracy: 0.9910891089108911</pre><h4>Learnings</h4><ol><li>KNN classifier will consume a lot of memory as size of training data grows.</li><li>The number of class and size of data will also affect the performance of the predictor.</li><li>Can be accurate with smaller training sets.</li><li>It needs trial and error with some informed guesstimates to select the correct distance function and number of neighbors.</li><li>It is best to optimize classifier performance with least number of classes and size of training data.</li><li>KNN Classifier does not need to one hot encode the labels and can predict the class labels.</li><li>Evaluation will take time proportional to number of records in test set</li><li>Can be performant to get single predictions.</li></ol><p>Checkout the notebook on Github</p><p><a href="https://github.com/mihirsaurkar/ML-apps/blob/c3b2e8a3391fcb11dee5143aab7887d5ad754f9a/Store_Classification/Store%20Classifier%20KNN.ipynb">https://github.com/mihirsaurkar/ML-apps/blob/c3b2e8a3391fcb11dee5143aab7887d5ad754f9a/Store_Classification/Store%20Classifier%20KNN.ipynb</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2d85f9adc5f7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Automated user migration and management of AWS Identity and Access Management (IAM) resources]]></title>
            <link>https://medium.com/@mihirsaurkar/automated-user-migration-and-management-of-aws-identity-and-access-management-iam-resources-42e835d2a53d?source=rss-2c7b00e38dbd------2</link>
            <guid isPermaLink="false">https://medium.com/p/42e835d2a53d</guid>
            <category><![CDATA[aws]]></category>
            <category><![CDATA[access-control]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[iam-roles]]></category>
            <category><![CDATA[cloud-computing]]></category>
            <dc:creator><![CDATA[Mihirsaurkar]]></dc:creator>
            <pubDate>Mon, 08 May 2023 00:58:41 GMT</pubDate>
            <atom:updated>2023-05-08T01:16:47.564Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jdK6qaygPGSPH-ajQu82ow.png" /></figure><h3>Introduction</h3><p>In this project based on a real-world scenario, I acted as Cloud Specialist with the mission to migrate users from existing on premise system to cloud infrastructure in an automated way and manage AWS IAM (Identity and Access Management) resources.</p><p>IAM (Identity and Access Management) is a crucial component of cloud security as it enables organizations to manage and secure access to their resources in a centralized manner. It allows administrators to create and manage policies and roles, granting or denying access to users and groups within an organization. IAM also provides audit logs and monitoring capabilities, allowing organizations to track and analyze user activity and identify potential security threats. Overall, IAM is a critical tool in maintaining a secure cloud environment and should be properly implemented and managed.</p><p>In this excercuse there were 100 users that needed to be migrated and have MFA (Multi-factor authentication) enabled on their accounts, as this is a security best practice.</p><p>To avoid repetitive and manual tasks in the AWS console, I needed to think about automating the processes.</p><figure><img alt="Solution Architecture" src="https://cdn-images-1.medium.com/max/1024/1*AE_ZAX_PBZlbp9eByk9L8g.png" /></figure><p>Before we start adding the users we need to look at their roles and anticipate the permissions they might need to perform their duties without any hindrances on the cloud environment seamlessly. To determine the appropriate IAM policies and roles for your AWS resources, it is important to follow the principle of least privilege. This means that you should grant users and services only the minimum permissions required to perform their tasks. To achieve this, you can start by identifying the specific actions and resources that each user or service needs to access. Once you have identified these, you can create custom policies or use AWS managed policies to grant only the necessary permissions. Additionally, it is important to regularly review and update the policies and roles to ensure that they remain relevant and that any unnecessary access is removed. By following this approach, you can help to ensure the security of your AWS resources and minimize the risk of unauthorized access or data breaches.</p><p>In this assignment I simulated 5 roles:</p><ul><li><strong>Cloud Administrator</strong>: This role is an administrator similar to system admin who would need almost unrestricted access to cloud resources.</li><li><strong>Linux Administrator:</strong> This role is Linux Administrator who can provision and manage virtual instances and manage their compute and memory capability moreover allow others to access them.</li><li><strong>Database Administrator (DBA):</strong> This is the traditional database administrator who would manage all the ins and outs of the cloud database instances.</li><li><strong>Network Administrator:</strong> This the network administrator responsible for managing VPC and setting access policies to it.</li><li><strong>Trainees:</strong> This is a special role assigned to anyone who needs access to view resources and services without worrying about modifying them</li></ul><h3><strong>Implementation</strong></h3><p>I started by mapping the roles to a logical user groups, and creating them on AWS console using the IAM service.</p><p>I created user groups as follows:</p><ul><li><strong>CloudAdministrator:</strong> This group has AWS managed policy attached to it which allows the user to have AWS administrator access to it.</li><li><strong>Linux Administrator: </strong>This group has the EC2 full access managed permission attached to it.</li><li><strong>Database Administrator (DBA): </strong>This group has the RDS full access managed permission attached to it.</li><li><strong>Network Administrator: </strong>This group has the VPC full access managed permission attached to it.</li><li><strong>Trainees: </strong>This group has the read only managed permission.</li></ul><p>In addition to above role specific policies we also add the policies to force the user to change their password on first login and force the users to set up MFA using an authenticator app supported by AWS.</p><p>In this case I received a list of email ids along with the user roles. I performed a quick mapping of the provided roles to the user groups I previously created on AWS IAM console. I reduced the email ids to loose the domain part and created the IAM user names.</p><p>Once I had the prepped file containing user name, role and assigned group I was ready to create the users on AWS.</p><p>There are several ways to do this in this approach I am using a shell script with an utility called dos2unix to read the contents of our user list and iteratively run the AWS cli to perform the following actions:</p><ul><li>Create the IAM user</li><li>Add properties to the created use namely reset password required, and a default password.</li><li>Add the new created user to the appropriate user group</li></ul><p>Note: Before we add users to a user group the group should be defined in IAM console.</p><p>To run the script I used the AWS cloud shell. Once the cloud shell start ups I uploaded my script along with the user list. Modified the permissions of the shell script to add execute permission and installed the dos2unix utility. Finally I executed the script providing the user list as input.</p><p>After a while the script was done executind and all the user are available on the IAM console. I can now email them the link to console and notify them of their default first time password.</p><p>Once they login they will be prompted to choose a strong password as defined in the password policy. Additionally since we added a custom MFA policy the users will have restricted access till they complete the MFA set up.</p><h3>Alternative Execution</h3><p>I could have authenticated my local console to aws and run the script locally by setting up the environment and downloading AWS cli. Using cloud shell freed up the overhead of local authentication and execution.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=42e835d2a53d" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>